De novato a experto: Períodos del mercado Forex
Contenido
Introducción
En mi ciudad, una vez me tomé un tiempo para observar cómo funciona el mercado local de hortalizas a diario, y lo que descubrí fue bastante revelador. Cada mañana, los agricultores llegan temprano con sus productos frescos, y los minoristas acuden igual de temprano para conseguir las mejores ofertas al por mayor. Durante las primeras horas tras la apertura del mercado, la actividad alcanza su punto álgido: las transacciones se producen con rapidez y los precios suelen fluctuar de forma dinámica. A medida que avanza el día y se acerca el mediodía y la hora de comer, el ritmo se va ralentizando poco a poco; llegan menos comerciantes y muchos agricultores empiezan a recoger. Sin embargo, a medida que se acerca la tarde y el mercado se aproxima a la hora de cierre, la actividad se intensifica una vez más, con compradores y vendedores de última hora realizando sus últimos movimientos antes de la puesta del sol.
Este sencillo comportamiento del mercado refleja de forma sorprendente cómo funciona el mercado Forex. Al igual que los agricultores y los minoristas, los operadores de todo el mundo operan dentro de sesiones bien definidas: períodos de alta y baja actividad que definen el ritmo del mercado. Existen horas punta en las que se realizan las transacciones más importantes, lo que determina el carácter y el ritmo de la jornada de trading.
El reto actual consiste en aprovechar MQL5 para visualizar estos periodos de sesión de mercado, alineándolos a la perfección con estructuras de marcos temporales superiores e inferiores. Al sincronizar la apertura y el cierre de estas sesiones, nuestro objetivo es comprender mejor cómo influye cada una en la siguiente y cómo el factor tiempo realmente impulsa el movimiento de los precios. Otro misterio fascinante por descubrir reside en la constatación de que las velas japonesas financieras representan barras basadas en el tiempo, que normalmente denotan períodos de horas, días o semanas. Aplicando el mismo concepto a las sesiones de mercado, podemos crear velas de sesión, cada una de las cuales refleja las características únicas de esa ventana de negociación. Al igual que los marcos temporales tradicionales, cada sesión posee su propia estructura de Apertura, Máximo, Mínimo y Cierre (OHLC), que cuenta una historia distinta de actividad, sentimiento y volatilidad dentro de ese período.
Sin embargo, la diferencia más notable es que las barras de sesión a menudo se superponen, especialmente en los mercados globales que comparten horarios de negociación. Por ejemplo, las sesiones de Londres y Nueva York (Fig. 1) se superponen durante varias horas, creando períodos de mayor volatilidad y liquidez. A diferencia de las barras de tiempo estándar que se suceden una tras otra en secuencia, las barras de sesión coexisten a lo largo del tiempo, lo que refleja la participación simultánea de múltiples regiones. Esta superposición es precisamente lo que da lugar a transiciones dinámicas en el sentimiento del mercado, lo que convierte al análisis basado en sesiones en un complemento esencial de las estructuras tradicionales basadas en el tiempo.

Figura 1: Superposición conceptual entre las sesiones de trading de Londres y Nueva York
Mediante este desarrollo, nuestro objetivo es visualizar las barras de sesión en el mismo lenguaje analítico que las velas japonesas estándar, lo que permitirá a los traders reconocer el comportamiento específico de cada sesión, identificar dónde se alcanzan los picos de liquidez y comparar cómo el sentimiento de una sesión influye en la siguiente. En definitiva, esta función conecta el análisis convencional de marcos temporales y el ritmo oculto de las sesiones de mercado, fomentando una conexión más profunda entre el tiempo, la estructura y la dinámica de precios.
Para dar vida a esta idea, a continuación describiré la implementación completa, explicando en detalle el concepto subyacente y presentando el código MQL5 completo en la siguiente sección de este análisis.
Implementación
Nuestra exploración de los periodos de mercado continúa evolucionando a medida que introducimos otra potente función: una que combina el análisis convencional de marcos temporales con la dinámica de las sesiones de negociación de Forex globales. Esta mejora reduce la brecha entre los períodos de tiempo estructurados y el comportamiento de las sesiones en el mundo real, ofreciendo una visión más realista de cómo evolucionan los mercados a lo largo del día. Para implementar esta nueva función, primero la desarrollaremos y probaremos de forma aislada, garantizando su precisión y rendimiento, antes de integrarla en el actual Market Periods Synchronizer. El resultado será una versión más avanzada y detallada de la herramienta, capaz de reflejar tanto la estructura temporal como el comportamiento del mercado en función de las sesiones, en perfecta armonía.
Otra razón para implementar esta función de forma aislada es garantizar que todos los lectores puedan comprender completamente el concepto antes de su integración. También constituye una buena práctica de desarrollo, especialmente a medida que el programa se vuelve más complejo con cada nueva función. Al aislar los componentes, facilitamos las pruebas, la depuración y la comprensión del sistema, tanto para los usuarios que siguen el desarrollo como para los desarrolladores que amplían la herramienta en futuras versiones.
Para poner en práctica este concepto, seguiremos un flujo de trabajo en dos pasos: en primer lugar, desarrollaremos la función de forma aislada y, a continuación, integraremos el componente validado en el EA «Market Periods Synchronizer». Este enfoque garantiza que la lógica siga siendo modular, fácil de probar y adaptable a futuras mejoras.
- Paso 1 — Desarrollo de forma aislada: Comenzamos creando una clase independiente CSessionVisualizer encargada de definir las sesiones de trading, calcular sus valores OHLC y representarlas como rectángulos rellenos que imitan las velas japonesas (incluyendo, opcionalmente, mechas y etiquetas). Esta versión independiente debe probarse por separado —por ejemplo, trazando las sesiones en un gráfico H1 de EURUSD— para verificar la precisión de los límites de las sesiones y la visualización de los datos.
- Paso 2 — Fase de integración: Una vez verificado, el visualizador de sesiones se puede integrar en el EA principal. Esto implica añadir nuevos controles de interfaz de usuario, como un botón de activación/desactivación «Mostrar sesiones: ON/OFF», un selector de color para las sesiones y un ajuste de historial para determinar cuántas sesiones anteriores se van a mostrar. La función RefreshLines() se ampliará para llamar al visualizador de sesiones tras trazar las líneas mayores y menores, lo que garantizará que todos los elementos permanezcan sincronizados. Las variables globales compartidas (como g_Lookback) mantendrán la coherencia, mientras que las superposiciones visuales entre sesiones se gestionarán mediante capas semitransparentes para mayor claridad.
Paso 1: Desarrollo de forma aislada
En esta sección, crearemos una clase modular llamada CSessionVisualizer, diseñada para detectar, calcular y visualizar las sesiones del mercado Forex en un gráfico, con cuerpos de velas, mechas opcionales y etiquetas. Este enfoque hace hincapié en la modularidad, la legibilidad y la reutilización, lo que permite integrar posteriormente la función en marcos más amplios, como el EA «Market Periods Synchronizer».
1. Configuración y finalidad del encabezado
Todo buen módulo de MQL5 comienza con un encabezado adecuado que defina claramente qué es el script, quién lo ha escrito y para qué sirve. Esto ayuda a los demás a entender tu código y garantiza unas buenas prácticas de desarrollo en lo que respecta al control de versiones.
También definimos una macro ARGB pequeña pero potente, que nos permite crear fácilmente colores con transparencia, algo esencial para superponer varias capas visuales (como sesiones que se solapan en un gráfico).
//+------------------------------------------------------------------+ //| SessionVisualizerTest.mqh | //| Modular class for Forex session OHLC visualization | //| Author: Clemence Benjamin | //| Version: 1.00 | //+------------------------------------------------------------------+ #property strict // ARGB Macro: Creates a color with alpha (transparency) #define ARGB(a,r,g,b) ((color)(((uchar)(a))<<24)|(((uchar)(r))<<16)|(((uchar)(g))<<8)|((uchar)(b))) #include <Object.mqh> // Provides access to chart object manipulation
En este caso, utilizamos Object.mqh porque el visualizador crea y gestiona varios objetos de gráfico (rectángulos, etiquetas, líneas). La macro ARGB() nos permite generar colores semitransparentes, como ARGB(120,255,0,0), para obtener un relleno rojo transparente, algo fundamental para la superposición visual. Pero más adelante eliminaremos el ARGB para utilizar uno de CCanvas.
2. Definiciones de sesión y configuración de la estructura
Antes de dibujar nada, debemos definir qué significa una "sesión" en nuestro contexto. Cada sesión de Forex (Sídney, Tokio, Londres y Nueva York) tiene su propio horario de apertura y cierre. Utilizamos una enumeración para definirlas simbólicamente y una estructura para almacenar las propiedades de cada sesión, como su nombre, horario, color y si está activa en ese momento.
enum SESSION_TYPE { SESSION_SYDNEY = 0, // 22:00-07:00 GMT SESSION_TOKYO = 1, // 00:00-09:00 GMT SESSION_LONDON = 2, // 08:00-17:00 GMT SESSION_NEWYORK = 3 // 13:00-22:00 GMT }; struct SessionInfo { SESSION_TYPE type; string name; // Short code e.g., "LON" int open_hour; // GMT open hour int close_hour; // GMT close hour color sess_color; // Visual color on chart bool enabled; // Whether to render this session };
El uso de enumeraciones garantiza que nuestro código sea fácil de leer y de ampliar posteriormente. Por ejemplo, agregar una "Sesión personalizada" solo requeriría una entrada adicional y la inicialización de la estructura.
3. Parámetros de entrada e inicialización de la clase
A continuación, definimos los parámetros de entrada y preparamos el constructor de nuestra clase. Esta configuración permite a los traders o desarrolladores personalizar los períodos de análisis histórico, los colores y el comportamiento de la mecha directamente desde el cuadro de diálogo de entrada del EA.
input int InpSessionLookback = 10; // Days of historical sessions input bool InpShowSessionWicks = false; input int InpWickAlpha = 120; input color InpFillBull = clrLime; input color InpFillBear = clrPink; class CSessionVisualizer : public CObject { private: SessionInfo m_sessions[4]; int m_gmt_offset; string m_prefix; public: CSessionVisualizer(string prefix = "SESS_") : m_prefix(prefix) { // Initialize all session parameters (GMT-based) m_sessions[SESSION_SYDNEY] = {SESSION_SYDNEY, "SYD", 22, 7, clrAqua, true}; m_sessions[SESSION_TOKYO] = {SESSION_TOKYO, "TOK", 0, 9, clrYellow, true}; m_sessions[SESSION_LONDON] = {SESSION_LONDON, "LON", 8, 17, clrRed, true}; m_sessions[SESSION_NEWYORK]= {SESSION_NEWYORK,"NY", 13, 22, clrBlue, true}; // Basic GMT offset estimation MqlDateTime dt; TimeToStruct(TimeCurrent(), dt); m_gmt_offset = -dt.hour % 24; } ~CSessionVisualizer() { DeleteAllSessionObjects(); }
En este caso, m_prefix ayuda a prevenir conflictos en la nomenclatura de objetos en el gráfico, especialmente cuando se utilizan varias herramientas simultáneamente. Por ejemplo, cada objeto puede estar etiquetado como "SESS_LON_O_1730764800".
4. Actualización y dibujo de sesiones
El método RefreshSessions() es el núcleo del visualizador. Borra los dibujos de la sesión anterior y los regenera durante un período de tiempo definido por el usuario. Este diseño modular permite actualizar los elementos visuales con cada nueva barra o intervalo de tiempo sin sobrecargar el gráfico.
void RefreshSessions(int lookback_days = 10) { DeleteAllSessionObjects(); // Clear previously drawn sessions datetime end_time = TimeCurrent(); datetime start_time = end_time - (lookback_days * 86400); for (int day = 0; day < lookback_days; day++) { datetime day_start = start_time + (day * 86400); for (int s = 0; s < 4; s++) { if (!m_sessions[s].enabled) continue; DrawSessionForDay(m_sessions[s], day_start); } } ChartRedraw(); }
Esta estructura se basa en bucles para mayor eficiencia y modularidad. Cada día y cada sesión se procesa individualmente, una técnica que evita la superposición de artefactos de sesión o la falta de rangos de datos.
5. Dibujo de sesiones individuales
La función DrawSessionForDay() se encarga de renderizar cada sesión. Determina las horas de apertura y cierre (ajustadas según la diferencia horaria con GMT), recupera los datos de precios para ese período y visualiza la sesión como un rectángulo similar a una vela japonesa con mechas opcionales.
void DrawSessionForDay(const SessionInfo &sess, datetime day_start) { MqlDateTime dt_open, dt_close; TimeToStruct(day_start, dt_open); dt_open.hour = sess.open_hour + m_gmt_offset; datetime t_open = StructToTime(dt_open); TimeToStruct(day_start, dt_close); dt_close.hour = sess.close_hour + m_gmt_offset; datetime t_close = StructToTime(dt_close); if (sess.close_hour < sess.open_hour) t_close += 86400; // Overnight fix double o,h,l,c; if (!GetOHLCInTimeRange(t_open, t_close, o,h,l,c)) return; color body_col = (c > o) ? InpFillBull : InpFillBear; string rect = m_prefix + sess.name + "_B_" + IntegerToString((int)t_open); ObjectCreate(0, rect, OBJ_RECTANGLE, 0, t_open, MathMin(o,c), t_close, MathMax(o,c)); ObjectSetInteger(0, rect, OBJPROP_BGCOLOR, body_col); ObjectSetInteger(0, rect, OBJPROP_FILL, true); ObjectSetInteger(0, rect, OBJPROP_BACK, true); }
Este enfoque nos proporciona barras de sesión que se comportan como velas japonesas: una nueva forma de analizar las sesiones como si fueran microperiodos de tiempo.
6. Recuperación de datos OHLC de la sesión
La función GetOHLCInTimeRange() extrae datos precisos de apertura, máximo, mínimo y cierre del período de tiempo seleccionado dentro de la duración de cada sesión. El uso de iBarShift() garantiza una alineación precisa con las barras del gráfico existentes.
bool GetOHLCInTimeRange(datetime start, datetime end, double &open, double &high, double &low, double &close) { int shift_start = iBarShift(_Symbol, _Period, start, false); int shift_end = iBarShift(_Symbol, _Period, end, false); if (shift_start < 0 || shift_end < 0) return false; int count = shift_start - shift_end + 1; if (count <= 0) return false; double o[],h[],l[],c[]; ArraySetAsSeries(o,true); ArraySetAsSeries(h,true); ArraySetAsSeries(l,true); ArraySetAsSeries(c,true); CopyOpen(_Symbol,_Period,shift_end,count,o); CopyHigh(_Symbol,_Period,shift_end,count,h); CopyLow(_Symbol,_Period,shift_end,count,l); CopyClose(_Symbol,_Period,shift_end,count,c); open = o[count-1]; high = h[ArrayMaximum(h,0,count)]; low = l[ArrayMinimum(l,0,count)]; close = c[0]; return true; }
Esto proporciona los datos OHLC de "velas japonesas de sesión" a partir de series de precios estándar, sin necesidad de volver a consultar fuentes externas.
7. Limpieza y gestión de objetos
Un buen código siempre se encarga de limpiar después de sí mismo. La función DeleteAllSessionObjects() garantiza que los objetos antiguos se eliminen al actualizar o desinicializar. Sin esto, acabarías con miles de rectángulos sobrantes después de largas sesiones de prueba.
void DeleteAllSessionObjects() { int total = ObjectsTotal(0); for (int i = total - 1; i >= 0; i--) { string name = ObjectName(0, i); if (StringFind(name, m_prefix) == 0) ObjectDelete(0, name); } }
Desarrollo del EA de prueba:
Comenzamos con un encabezado compacto y la inclusión. Este Asesor Experto (EA) es intencionalmente pequeño: solo inicia el visualizador, pone en marcha un temporizador para actualizaciones periódicas (para que puedas ver cómo se actualizan las sesiones mientras el mercado está en marcha) y realiza la limpieza al salir. Mantener los entornos de prueba pequeños ayuda a aislar problemas y validar el comportamiento de las clases antes de integrarlos en sistemas más grandes.
//+------------------------------------------------------------------+ //| SessionTest.mq5 - Standalone test for CSessionVisualizer | //| Purpose: lightweight EA to exercise the SessionVisualizer class | //+------------------------------------------------------------------+ #property strict #include <SessionVisualizerTest.mqh>
A continuación, declaramos la instancia de tiempo de ejecución. El uso de un puntero y new proporciona un control explícito sobre el ciclo de vida: creamos el visualizador en OnInit() y lo eliminamos en OnDeinit(), de modo que el destructor se ejecuta inmediatamente y elimina los objetos que la clase creó. Este patrón resulta práctico para realizar pruebas y refleja la forma en que se gestionan los objetos en sistemas de trading más amplios que crean o destruyen subsistemas de forma dinámica.
// runtime pointer to the session visualizer CSessionVisualizer *g_session_vis = NULL;
En OnInit() es donde construimos el visualizador, configuramos un temporizador de actualización y realizamos el dibujo inicial. El ejemplo utiliza un temporizador de demostración de 30 segundos, lo suficientemente corto como para ver las actualizaciones con frecuencia, pero lo suficientemente largo como para evitar un uso excesivo de la CPU. Le pasamos un prefijo único al visualizador para que sus objetos tengan un espacio de nombres claro (algo útil al probar varias herramientas en el mismo gráfico). Llamar inmediatamente a RefreshSessions(5) renderizará cinco días de sesiones para que pueda verificar de inmediato la representación histórica de las sesiones.
int OnInit() { // allocate the session visualizer with a test-specific prefix to avoid name clashes g_session_vis = new CSessionVisualizer("TEST_SESS_"); // Refresh every 30 seconds during testing (demo). Adjust for production usage. EventSetTimer(30); // initial rendering of the last 5 days (quick sanity check) if(g_session_vis != NULL) g_session_vis.RefreshSessions(5); return(INIT_SUCCEEDED); }
Nota: Realizamos una comprobación de nulidad antes de llamar al método. Si la orden «new» falla por cualquier motivo (algo muy poco habitual en ordenadores de sobremesa normales), esto evita que el sistema se bloquee y facilita la detección del error en los registros.
OnTimer() controla las actualizaciones periódicas. Para la visualización de sesiones, esto es útil porque los OHLC de la sesión pueden cambiar ligeramente en las barras en vivo (dependiendo del marco temporal que se utilice para calcular el OHLC), y los redibujos frecuentes permiten observar la evolución de las mechas y los cuerpos de la sesión. En un EA de producción, es posible que prefiera actualizar solo con barras nuevas o con un intervalo más largo, pero para las pruebas, 30 segundos es suficiente.
void OnTimer() { // periodic refresh of the last 5 days if(g_session_vis != NULL) g_session_vis.RefreshSessions(5); }
OnDeinit() realiza la limpieza: detenemos el temporizador y eliminamos el objeto visualizador. Al eliminar el objeto, se invoca su destructor, que (tal como está implementado) elimina todos los objetos de sesión que la clase creó en el gráfico, lo que garantiza que el gráfico permanezca limpio después de la prueba. La limpieza explícita es esencial en entornos de carga repetida (carga/descarga de muchos EA) para evitar la sobrecarga del gráfico y la persistencia de objetos.
void OnDeinit(const int reason) { EventKillTimer(); if(g_session_vis != NULL) { // destructor will call DeleteAllSessionObjects() delete g_session_vis; g_session_vis = NULL; } }
Resultado de la prueba inicial:
Nuestra fase de pruebas inicial fue sencilla: tras compilar correctamente el Asesor Experto de Prueba de Sesión, simplemente lo adjuntamos a un gráfico real de MetaTrader 5. El resultado fue una visualización inmediata y clara de las sesiones de Forex. En la imagen de abajo, se puede ver claramente cómo se superponen las sesiones, cada una distinguida por su propio color para facilitar su identificación. Los rectángulos de sesión rellenos también transmiten el sentimiento del mercado: el verde lima indica un cierre de sesión alcista, mientras que el rosa marca uno bajista. Esta visualización codificada por colores permite interpretar fácilmente la tendencia dominante de cada sesión de negociación de un vistazo.

Figura 2: Prueba de sesión
Paso 2: Fase de integración
Toda gran herramienta de trading pasa por una fase de transformación: desde un script experimental hasta un módulo de producción totalmente integrado. Nuestro visualizador de sesiones no es una excepción. Originalmente, el encabezado de prueba (SessionVisualizerTest.mqh) se centraba en establecer la base: definir las sesiones de negociación, recuperar los datos OHLC y dibujar rectángulos de colores que representaran cada período del mercado Forex. Si bien cumplió bien su función durante las pruebas, la integración en producción de nuestro Market Periods Synchronizer EA requirió mejoras para lograr una mayor claridad visual, extensibilidad y compatibilidad.
Por qué dejamos de usar cuerpos de sesión rellenos
En la versión de prueba, las sesiones se representaban con rectángulos rellenos de color sólido, utilizando los colores de cada sesión para resaltar cada bloque de negociación activo. Este enfoque parecía dinámico, pero rápidamente se volvió visualmente congestionado, especialmente cuando se mostraban sesiones superpuestas o capas históricas juntas. El estilo de relleno también ocultaba los detalles subyacentes del gráfico, lo que frustraba uno de nuestros objetivos: proporcionar contexto sin saturar la información.
Por lo tanto, en la nueva versión, introducimos rectángulos de cuerpo sin relleno con bordes punteados. Estas velas japonesas actúan como velas huecas, marcando el rango de apertura y cierre de cada sesión sin cubrir el gráfico de precios. La elección del diseño hace que el gráfico sea más legible y garantiza que el trader pueda seguir viendo claramente la acción del precio con claridad debajo de los marcadores de sesión.
Como desarrolladores, esta es una lección fundamental: a veces, menos color equivale a más información. Al eliminar los rellenos opacos, ganamos visibilidad y profesionalismo en la presentación.
Integración de CCanvas y ARGB: Un único motor visual
Un cambio técnico sutil pero importante en el nuevo encabezado es el uso de #include<Canvas/Canvas.mqh> para imágenes basadas en ARGB. Esto garantiza la compatibilidad con la función CCanvas existente de nuestro EA, que también gestiona la representación del fondo y la transparencia para otros módulos. En lugar de redefinir ARGB por separado, la nueva versión delega esta función a la biblioteca Canvas, lo que crea coherencia en todos los gráficos utilizados por EA.
Esta decisión es tanto arquitectónica como práctica. Al crear EAs modulares que reutilizan componentes, las macros ARGB duplicadas pueden provocar conflictos o un comportamiento impredecible, especialmente si un componente define la transparencia de manera diferente. El uso de un único motor compartido, CCanvas, ayuda a mantener una lógica de renderizado unificada y reduce los costes de mantenimiento.
En pocas palabras, declaramos la inclusión de Canvas de forma global para que cada componente visual, ya sean cuadros de sesión, mechas o superposiciones, utilice el mismo "lenguaje" gráfico.
Redefiniendo las sesiones: De bloques de color a marcos de mercado
La estructura de la representación de la sesión sigue siendo similar: seguimos utilizando una estructura SessionInfo que contiene el nombre, el tipo, las horas de apertura/cierre y el color. Sin embargo, hemos reinventado la forma en que se visualizan estos atributos.
Cada sesión ahora genera:
- Un rectángulo punteado que marca el rango de precios de apertura y cierre.
- Rectángulos opcionales rellenos de gris que muestran la excursión total máxima-mínima de la sesión.
- Etiquetas de texto y líneas verticales que marcan los horarios de apertura y cierre.
- Superposición de primer plano y fondo para que los rectángulos permanezcan detrás de las velas de precios mientras que las líneas y etiquetas permanecen visibles para el usuario.
Esto crea una apariencia analítica pero elegante, como un plano transparente del ritmo del mercado. Los traders pueden ver qué sesiones se están expandiendo o contrayendo sin perder detalle en el gráfico. El nuevo concepto de "espacio vacío" actúa como un marco para el mercado, en lugar de una capa que cubra el mercado.
Configuración en tiempo de ejecución: Cómo hacer que el visualizador sea dinámico.
Para brindar mayor flexibilidad a los usuarios y a los asesores expertos, hemos convertido configuraciones que antes eran estáticas en variables de tiempo de ejecución. Por ejemplo, ahora se pueden configurar globalmente las variables m_show_wicks, m_wick_alpha y m_gmt_offset. Un operador o desarrollador puede activar o desactivar la visibilidad de la mecha, cambiar la transparencia o ajustar el desfase horario sin necesidad de recompilar.
Notarás funciones como
void SetShowWicks(bool show) { m_show_wicks = show; } void SetWickAlpha(int alpha) { m_wick_alpha = MathMax(0, MathMin(255, alpha)); } void SetGMTOffset(int offset){ m_gmt_offset = offset; }
Estas pequeñas pero significativas adiciones permiten la interacción en tiempo real entre la interfaz de EA y la capa de visualización, un aspecto fundamental del diseño modular moderno en MQL5.
Información contextual: Visualización de las sesiones actuales y anteriores
A diferencia de la versión de prueba, que solo mostraba sesiones históricas estáticas, el nuevo encabezado introduce el seguimiento de las sesiones actuales y recientes. Durante las operaciones en tiempo real, el sistema identifica qué sesión está activa y le añade la etiqueta "En vivo". Además, mantiene visible la sesión que se cerró recientemente durante una hora después de su cierre, lo que ofrece una referencia visual rápida del rendimiento de la sesión anterior.
Ahora que comprendemos los ajustes realizados para lograr una integración efectiva, procederé a desglosar y explicar en detalle el nuevo encabezado SessionVisualizer. A continuación, hablaré sobre su integración en el Asesor Experto (EA) y destacaré las nuevas funciones del EA diseñadas para ayudar a los traders a aprovechar de forma más eficiente los periodos de sesión del mercado y así tomar mejores decisiones comerciales.
1) Descripción del módulo y rigor de compilación
Comenzamos con una descripción clara porque el código publicado también es documentación. El control de versiones informa a los lectores sobre las novedades (por ejemplo, "rectángulos sin relleno, bordes punteados y mechas grises"), y #property strict garantiza la seguridad de tipos de MQL5 moderna, detectando errores silenciosos que a menudo se cuelan en los encabezados experimentales.
//+------------------------------------------------------------------+ //| SessionVisualizer.mqh | //| Modular class for Forex session OHLC visualization | //| Copyright 2025: Clemence Benjamin | //| Version: 1.02 | //+------------------------------------------------------------------+ #property strict
2) Dependencias compartidas (API de objetos + ARGB unificado a través de Canvas)
En la fase de pruebas, es habitual crear nuestra propia macro ARGB; en producción, nos adaptamos al motor visual único de EA para evitar conflictos. La inclusión de Canvas.mqh nos proporciona el estándar ARGB() y una compatibilidad perfecta con otras funciones de dibujo en el Asesor Experto Market Periods Synchronizer.
#include <Object.mqh> #include <Canvas/Canvas.mqh> // for ARGB()
3) Modelo de dominio de sesión (enumeración + estructura)
Formalizamos el espacio del problema: cuatro sesiones principales, nombres fáciles de entender para los humanos y un contenedor SessionInfo sencillo. Aunque una propiedad no se utilice en todas partes (por ejemplo, sess_color ya no colorea el relleno del cuerpo), la mantenemos: los colores siguen controlando las etiquetas/líneas y manteniendo la compatibilidad con versiones anteriores de herramientas y hábitos de usuario.
//---------------------------------------------- // Session enum and info //---------------------------------------------- enum SESSION_TYPE { SESSION_SYDNEY = 0, // 22:00-07:00 GMT SESSION_TOKYO = 1, // 00:00-09:00 GMT SESSION_LONDON = 2, // 08:00-17:00 GMT SESSION_NEWYORK= 3 // 13:00-22:00 GMT }; struct SessionInfo { SESSION_TYPE type; string name; // "SYD","TOK","LON","NY" int open_hour; // GMT open hour int close_hour; // GMT close hour color sess_color; // base tint (not used for border now) bool enabled; };
4) Parámetros de ejecución (parámetros ajustables por el EA)
Declaramos variables globales y ajustables mediante EA para que las funcionalidades se activen o desactiven sin necesidad de recompilar. La propiedad lookback controla la densidad del historial, m_show_wicks/m_wick_alpha controlan la visibilidad y la transparencia de los rangos de mechas, y m_gmt_offset permite que el EA armonice la hora del servidor con GMT, el clásico punto problemático del mundo real.
//---------------------------------------------- // Runtime params (configured by EA) //---------------------------------------------- int m_session_lookback = 10; bool m_show_wicks = false; int m_wick_alpha = 120; int m_gmt_offset = 0;
5) Estructura de clase y valores predeterminados del constructor
Mantenemos la API pública compacta y el estado privado explícito. En el constructor, inicializamos los horarios GMT canónicos y las etiquetas legibles de tres letras. Observará que almacenamos un prefijo m_prefix: este es el secreto para nombrar los objetos de forma segura y eliminarlos correctamente más adelante, evitando colisiones accidentales con otros objetos del gráfico.
class CSessionVisualizer : public CObject { private: SessionInfo m_sessions[4]; string m_prefix; public: CSessionVisualizer(string prefix="SESS_") : m_prefix(prefix) { // Defaults (GMT schedule) m_sessions[SESSION_SYDNEY] .type=SESSION_SYDNEY; m_sessions[SESSION_SYDNEY] .name="SYD"; m_sessions[SESSION_SYDNEY] .open_hour=22; m_sessions[SESSION_SYDNEY] .close_hour=7; m_sessions[SESSION_SYDNEY] .sess_color=clrAqua; m_sessions[SESSION_SYDNEY] .enabled=true; m_sessions[SESSION_TOKYO] .type=SESSION_TOKYO; m_sessions[SESSION_TOKYO] .name="TOK"; m_sessions[SESSION_TOKYO] .open_hour=0; m_sessions[SESSION_TOKYO] .close_hour=9; m_sessions[SESSION_TOKYO] .sess_color=clrYellow; m_sessions[SESSION_TOKYO] .enabled=true; m_sessions[SESSION_LONDON] .type=SESSION_LONDON; m_sessions[SESSION_LONDON] .name="LON"; m_sessions[SESSION_LONDON] .open_hour=8; m_sessions[SESSION_LONDON] .close_hour=17; m_sessions[SESSION_LONDON] .sess_color=clrRed; m_sessions[SESSION_LONDON] .enabled=true; m_sessions[SESSION_NEWYORK].type=SESSION_NEWYORK; m_sessions[SESSION_NEWYORK].name="NY"; m_sessions[SESSION_NEWYORK].open_hour=13; m_sessions[SESSION_NEWYORK].close_hour=22; m_sessions[SESSION_NEWYORK].sess_color=clrBlue; m_sessions[SESSION_NEWYORK].enabled=true; } ~CSessionVisualizer() { DeleteAllSessionObjects(); }
6) API pública: actualización + interruptores (diseñada para gráficos en tiempo real)
RefreshSessions() es la función de reconstrucción con un solo botón: borra, vuelve a dibujar el historial y luego dibuja la ventana en vivo. Ponemos a disposición pequeños y expresivos métodos de configuración para que un panel de EA pueda cambiar de sesión, modificar el color de los temas o sincronizar la hora GMT sobre la marcha. Un cambio notable con respecto al encabezado de prueba: no hay rellenos de cuerpo; SetFillColors() ahora es una operación nula que se mantiene únicamente por compatibilidad con versiones anteriores.
//--------------------------------------------------------------- // PUBLIC API //--------------------------------------------------------------- void RefreshSessions(int lookback_days=10) { m_session_lookback = lookback_days; DeleteAllSessionObjects(); const datetime now = TimeCurrent(); const datetime start = now - (m_session_lookback*86400); // Historical completed sessions for(int d=0; d<m_session_lookback; ++d) { datetime day_start = start + d*86400; for(int s=0; s<4; ++s) { if(!m_sessions[s].enabled) continue; DrawHistoricalSessionForDay(m_sessions[s], day_start); } } // Active session windows DrawCurrentSessions(); ChartRedraw(); } void SetSessionEnabled(SESSION_TYPE type, bool enabled) { for(int i=0;i<4;i++) if(m_sessions[i].type==type){ m_sessions[i].enabled=enabled; break; } } void SetSessionColor(SESSION_TYPE type, color col) { for(int i=0;i<4;i++) if(m_sessions[i].type==type){ m_sessions[i].sess_color=col; break; } } // Kept for compatibility with EA; ignored (we no longer fill bodies). void SetFillColors(color /*bull*/, color /*bear*/) { /* no-op */ } void SetShowWicks(bool show) { m_show_wicks = show; } void SetWickAlpha(int alpha) { m_wick_alpha = MathMax(0, MathMin(255, alpha)); } void SetGMTOffset(int offset){ m_gmt_offset = offset; } // public cleanup void ClearAll() { DeleteAllSessionObjects(); }
7) Dibujo histórico (solo sesiones completadas)
Un error común en las herramientas de sesión es incorporar datos parciales al "historial". Aquí calculamos la apertura/cierre con m_gmt_offset, gestionamos el caso de "desplazamiento a medianoche" y luego omitimos todo lo que no esté completamente en el pasado. Solo después de confirmar que la ventana está completa, calculamos OHLC y renderizamos.
private: //--------------------------------------------------------------- // CORE DRAWING //--------------------------------------------------------------- void DrawHistoricalSessionForDay(const SessionInfo &sess, datetime day_start) { // build open/close datetimes adjusted by GMT offset MqlDateTime dto, dtc; TimeToStruct(day_start, dto); dtc = dto; dto.hour = sess.open_hour + m_gmt_offset; dto.min=0; dto.sec=0; dtc.hour = sess.close_hour + m_gmt_offset; dtc.min=0; dtc.sec=0; datetime t_open = StructToTime(dto); datetime t_close = StructToTime(dtc); if(sess.close_hour < sess.open_hour) t_close += 86400; // wraps midnight // Only draw if fully in the past if(t_close >= TimeCurrent()) return; if(t_close < TimeCurrent() - (m_session_lookback*86400)) return; double o,h,l,c; if(!GetOHLCInTimeRange(t_open, t_close, o,h,l,c)) return; DrawSessionVisuals(sess, t_open, t_close, o,h,l,c, /*is_current=*/false); }
8) Gestión de sesiones en directo (y 1 hora de margen para la última).
Detectamos la sesión activa en tiempo real creando la ventana de tiempo del día y ajustándola para las sesiones nocturnas. Si estamos dentro, transmitimos OHLC hasta ahora, ajustamos h/l con el precio bid actual para que el recuadro refleje la evolución del precio en tiempo real y lo etiquetamos como “(Live)”. Además, mantenemos la sesión recién cerrada en pantalla durante una hora adicional, lo cual resulta muy útil durante el rollover, cuando los traders necesitan contexto.
void DrawCurrentSessions() { const datetime now = TimeCurrent(); MqlDateTime cur; TimeToStruct(now, cur); for(int i=0;i<4;i++) { if(!m_sessions[i].enabled) continue; const SessionInfo sess = m_sessions[i]; // compute session window for "today" (adjusted for wrap) MqlDateTime ds, de; TimeToStruct(now, ds); de = ds; ds.hour = sess.open_hour + m_gmt_offset; ds.min=0; ds.sec=0; de.hour = sess.close_hour + m_gmt_offset; de.min=0; de.sec=0; datetime session_start = StructToTime(ds); datetime session_end = StructToTime(de); if(sess.close_hour < sess.open_hour) { if(cur.hour >= sess.open_hour + m_gmt_offset) session_end += 86400; // ends tomorrow else session_start -= 86400; // started yesterday } if(now >= session_start && now <= session_end) { // pull OHLC until now (close = current) double o,h,l,c; if(!GetOHLCInTimeRange(session_start, now, o,h,l,c)) o = h = l = c = SymbolInfoDouble(_Symbol, SYMBOL_BID); const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); h = MathMax(h, bid); l = MathMin(l, bid); c = bid; DrawSessionVisuals(sess, session_start, session_end, o,h,l,c, /*is_current=*/true); } else if(now > session_end && now <= session_end + 3600) { // Keep just-closed session visible for 1 hour double o,h,l,c; if(!GetOHLCInTimeRange(session_start, session_end, o,h,l,c)) continue; DrawSessionVisuals(sess, session_start, session_end, o,h,l,c, /*is_current=*/false); } } }
9) Gramática visual: cuerpos sin relleno, bordes punteados, mecha gris opcional
Este es el cambio más característico respecto al encabezado de prueba. Los cuerpos están vacíos y colocados detrás del precio (OBJPROP_BACK=true) para que nada oculte las velas. Los bordes codifican el sentimiento (verde oscuro para alcista, rojo para bajista; de lo contrario, color de sesión). Cuando están habilitadas, las mechas son rectángulos ARGB de color gris suave dibujados en el fondo para revelar todo el recorrido entre el máximo y el mínimo sin distracciones. Las etiquetas y las líneas de tiempo permanecen en primer plano para mayor claridad.
// Renders: // - unfilled body rectangle (Open..Close) with dashed border // * border = clrDarkGreen if bullish, clrRed if bearish (Neutral uses session color) // - optional GREY filled wick rectangles in background for visibility // - open/close vlines + labels in foreground (thin) void DrawSessionVisuals(const SessionInfo &sess, datetime t_open, datetime t_close, double o,double h,double l,double c, bool is_current) { const bool bullish = (c > o); const bool bearish = (c < o); // ---------- vertical lines ---------- string vopen = m_prefix + sess.name + "_O" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open); if(ObjectFind(0, vopen) == -1) ObjectCreate(0, vopen, OBJ_VLINE, 0, t_open, 0); ObjectSetInteger(0, vopen, OBJPROP_COLOR, sess.sess_color); ObjectSetInteger(0, vopen, OBJPROP_WIDTH, is_current?2:1); ObjectSetInteger(0, vopen, OBJPROP_STYLE, is_current?STYLE_SOLID:STYLE_DASH); ObjectSetInteger(0, vopen, OBJPROP_BACK, false); ObjectSetInteger(0, vopen, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, vopen, OBJPROP_HIDDEN, false); string vclose = m_prefix + sess.name + "_C" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_close); if(ObjectFind(0, vclose) == -1) ObjectCreate(0, vclose, OBJ_VLINE, 0, t_close, 0); ObjectSetInteger(0, vclose, OBJPROP_COLOR, sess.sess_color); ObjectSetInteger(0, vclose, OBJPROP_WIDTH, is_current?2:1); ObjectSetInteger(0, vclose, OBJPROP_STYLE, is_current?STYLE_SOLID:STYLE_DASH); ObjectSetInteger(0, vclose, OBJPROP_BACK, false); ObjectSetInteger(0, vclose, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, vclose, OBJPROP_HIDDEN, false); // ---------- body rectangle (UNFILLED, dashed) ---------- const double body_top = MathMax(o,c); const double body_bot = MathMin(o,c); color border_col = sess.sess_color; // neutral fallback if(bullish) border_col = clrDarkGreen; else if(bearish) border_col = clrRed; string rect = m_prefix + sess.name + "_R" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open); if(ObjectFind(0, rect) == -1) { if(!ObjectCreate(0, rect, OBJ_RECTANGLE, 0, t_open, body_bot, t_close, body_top)) PrintFormat("Failed to create session rectangle %s err=%d", rect, GetLastError()); } // unfilled rectangle with dashed edge, kept in background ObjectSetInteger(0, rect, OBJPROP_FILL, false); ObjectSetInteger(0, rect, OBJPROP_COLOR, border_col); ObjectSetInteger(0, rect, OBJPROP_STYLE, STYLE_DASH); ObjectSetInteger(0, rect, OBJPROP_WIDTH, 1); ObjectSetInteger(0, rect, OBJPROP_BACK, true); ObjectSetInteger(0, rect, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, rect, OBJPROP_HIDDEN, false); // keep coordinates fresh ObjectMove(0, rect, 0, t_open, body_bot); ObjectMove(0, rect, 1, t_close, body_top); // ---------- WICKS (optional) as GREY filled rectangles in background ---------- if(m_show_wicks) { uint wick_col = ARGB(m_wick_alpha, 128,128,128); // semi-transparent grey // Upper wick: [body_top .. high] string wu = m_prefix + sess.name + "_WU" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open); if(h > body_top) { if(ObjectFind(0, wu) == -1) { if(!ObjectCreate(0, wu, OBJ_RECTANGLE, 0, t_open, body_top, t_close, h)) PrintFormat("Failed to create upper wick %s err=%d", wu, GetLastError()); } ObjectSetInteger(0, wu, OBJPROP_BGCOLOR, wick_col); ObjectSetInteger(0, wu, OBJPROP_COLOR, wick_col); ObjectSetInteger(0, wu, OBJPROP_FILL, true); ObjectSetInteger(0, wu, OBJPROP_BACK, true); ObjectSetInteger(0, wu, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, wu, OBJPROP_HIDDEN, false); ObjectMove(0, wu, 0, t_open, body_top); ObjectMove(0, wu, 1, t_close, h); } else ObjectDelete(0, wu); // Lower wick: [low .. body_bot] string wl = m_prefix + sess.name + "_WL" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open); if(l < body_bot) { if(ObjectFind(0, wl) == -1) { if(!ObjectCreate(0, wl, OBJ_RECTANGLE, 0, t_open, l, t_close, body_bot)) PrintFormat("Failed to create lower wick %s err=%d", wl, GetLastError()); } ObjectSetInteger(0, wl, OBJPROP_BGCOLOR, wick_col); ObjectSetInteger(0, wl, OBJPROP_COLOR, wick_col); ObjectSetInteger(0, wl, OBJPROP_FILL, true); ObjectSetInteger(0, wl, OBJPROP_BACK, true); ObjectSetInteger(0, wl, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, wl, OBJPROP_HIDDEN, false); ObjectMove(0, wl, 0, t_open, l); ObjectMove(0, wl, 1, t_close, body_bot); } else ObjectDelete(0, wl); } else { // Ensure wicks are removed if disabled ObjectDelete(0, m_prefix + sess.name + "_WU" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open)); ObjectDelete(0, m_prefix + sess.name + "_WL" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open)); } // ---------- Labels in foreground ---------- string session_tag = sess.name + (is_current ? " (Live)" : ""); string lbl_o = m_prefix + sess.name + "_OL" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open); if(ObjectFind(0, lbl_o) == -1) ObjectCreate(0, lbl_o, OBJ_TEXT, 0, t_open, o); ObjectSetString (0, lbl_o, OBJPROP_TEXT, session_tag + " Open"); ObjectSetInteger(0, lbl_o, OBJPROP_COLOR, sess.sess_color); ObjectSetInteger(0, lbl_o, OBJPROP_FONTSIZE, 8); ObjectSetInteger(0, lbl_o, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, lbl_o, OBJPROP_HIDDEN, false); ObjectSetInteger(0, lbl_o, OBJPROP_BACK, false); string lbl_c = m_prefix + sess.name + "_CL" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_close); if(ObjectFind(0, lbl_c) == -1) ObjectCreate(0, lbl_c, OBJ_TEXT, 0, t_close, c); ObjectSetString (0, lbl_c, OBJPROP_TEXT, session_tag + " Close"); ObjectSetInteger(0, lbl_c, OBJPROP_COLOR, sess.sess_color); ObjectSetInteger(0, lbl_c, OBJPROP_FONTSIZE, 8); ObjectSetInteger(0, lbl_c, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, lbl_c, OBJPROP_HIDDEN, false); ObjectSetInteger(0, lbl_c, OBJPROP_BACK, false); }
10) Agregación OHLC robusta (basada en el desplazamiento de barras)
Para garantizar la exactitud en diferentes intervalos de tiempo, calculamos el OHLC de la sesión utilizando los límites de iBarShift y obtenemos una porción compacta de matrices; luego, las agregamos con ArrayMaximum/Minimum. Esto evita errores de desfase de uno y funciona incluso si la granularidad del marco temporal no coincide perfectamente con los límites de la sesión.
//--------------------------------------------------------------- // Data helpers //--------------------------------------------------------------- bool GetOHLCInTimeRange(datetime start, datetime end, double &open, double &high, double &low, double &close) { int shift_start = iBarShift(_Symbol, _Period, start, false); int shift_end = iBarShift(_Symbol, _Period, end, false); if(shift_start < 0 || shift_end < 0) return false; int bars = shift_start - shift_end + 1; if(bars <= 0) return false; double opens[], highs[], lows[], closes[]; ArraySetAsSeries(opens, true); ArraySetAsSeries(highs, true); ArraySetAsSeries(lows, true); ArraySetAsSeries(closes, true); if(CopyOpen (_Symbol,_Period, shift_end, bars, opens) != bars) return false; if(CopyHigh (_Symbol,_Period, shift_end, bars, highs) != bars) return false; if(CopyLow (_Symbol,_Period, shift_end, bars, lows) != bars) return false; if(CopyClose(_Symbol,_Period, shift_end, bars, closes) != bars) return false; open = opens[bars-1]; high = highs[ArrayMaximum(highs,0,bars)]; low = lows [ArrayMinimum(lows ,0,bars)]; close = closes[0]; return true; }
11) Limpieza determinista (recolección de basura con ámbito de prefijo)
Las herramientas profesionales para la creación de gráficos deben dejar el escenario limpio. Al actualizar o eliminar EA, iteramos los objetos en orden inverso y eliminamos solo aquellos que llevan nuestro prefijo m_prefix. Esto implica una convivencia respetuosa con otros indicadores/EAs y evita los "dibujos fantasma" que frustran a los usuarios.
//--------------------------------------------------------------- // Cleanup //--------------------------------------------------------------- void DeleteAllSessionObjects() { int total = ObjectsTotal(0); for(int i = total-1; i >= 0; --i) { string name = ObjectName(0, i); if(StringFind(name, m_prefix) == 0) ObjectDelete(0, name); } } };
Integración de CSessionVisualizer y la información de sesión en MarketPeriodsSynchronizerEA
Antes de esta actualización, nuestra versión anterior de EA se centraba en la estructura de períodos mayores/menores, rellenos de cuerpo, mechas y controles de interfaz de usuario, sin ninguna noción de sesiones de mercado o resúmenes de sesión. En esta versión, incorporamos la nueva clase CSessionVisualizer y añadimos un panel compacto de "Información de sesión", además de prácticos interruptores de control para que las sesiones y sus resaltados de mecha se puedan gestionar independientemente de los elementos visuales de los períodos principales. A continuación, les explicaré la integración paso a paso, como si estuviéramos evolucionando la versión anterior de EA, indicando el motivo de cada cambio y pegando el fragmento exacto que lo implementa. (Como referencia, la versión base anterior de EA es la v1.01, sin sesiones y sin el panel de información).
1) Integrar el visualizador de sesiones en EA
Incluimos el encabezado de nuestro nuevo renderizador de sesión junto con el asistente Canvas. El encabezado de prueba ya utiliza CSessionVisualizer con rectángulos de cuerpo punteados sin relleno y rellenos de mecha grises opcionales en el fondo, por lo que visualmente no entrará en conflicto con nuestras superposiciones HTF. Mantenemos las inclusiones mínimas y explícitas para evitar colisiones de nombres/ODR.
#include <Canvas/Canvas.mqh> // Canvas helper library (expects Canvas.mqh to be present) #include <SessionVisualizer.mqh> // Integrated Session Visualizer class
2) Nuevas aportaciones para las sesiones y el panel informativo.
Los insumos son el contrato con los usuarios. Agregamos interruptores específicos para mostrar las sesiones, su ventana de análisis retrospectivo, el desfase GMT del broker (para que las sesiones coincidan), la visibilidad/alfa de la mecha solo para las sesiones y el indicador para el panel "Información de la sesión". Observe que separamos deliberadamente la configuración de las mechas para los pares principales y las sesiones; los traders a menudo desean mechas para unos, pero no para los otros.
// Session inputs input bool InpShowSessions = true; // Show Forex sessions input int InpSessionLookback = 10; // Session lookback (days) input int InpGMTOffset = 0; // GMT offset (broker hours) // Wick visualization (separated for majors and sessions) input bool InpShowSessionWicks = false; // Show wick regions for sessions input int InpSessionWickAlpha = 120; // Alpha transparency for session wicks 0..255 // Session information panel input bool InpShowSessionInfo = true; // Show session info panel
Las versiones anteriores no tenían entradas específicas para cada sesión; todo giraba en torno a elementos principales y secundarios, y un interruptor para la mecha. Ahora separamos esas preocupaciones para que el diseño se mantenga limpio incluso cuando ambas funciones estén activas.
3) Las variables globales de tiempo de ejecución reflejan las nuevas entradas.
El panel de control del EA modifica el estado en tiempo de ejecución, por lo que mantenemos copias mutables de las entradas. Aquí añadimos g_ShowSessions, session-wick runtime (g_ShowSessionWicks, g_SessionWickAlpha) y un indicador para el panel de información. Mantener estas variables cerca de las demás variables globales preserva el modelo mental: entradas → tiempo de ejecución → interfaz de usuario.
// Wick runtime (separated for majors and sessions) bool g_ShowWicks; int g_WickAlpha; bool g_ShowSessionWicks; int g_SessionWickAlpha; // Session runtime bool g_ShowSessions; // Session information panel bool g_ShowSessionInfo;
4) Una única instancia compartida de CSessionVisualizer
Todos los dibujos de sesión deben compartir un prefijo de nomenclatura y un ciclo de vida estables. Instanciamos un visualizador con un prefijo corto para que los nombres de los objetos sigan siendo únicos y fáciles de eliminar sin afectar a otros objetos del gráfico.
// Session visualizer instance CSessionVisualizer g_sessions("SESS_");
5) Información de la sesión: estado compacto que podemos actualizar en cada ciclo
A los traders les encanta tener el contexto a la vista: "¿En qué sesión estoy?" ¿Cómo cerró la anterior? Almacenamos los datos mínimos que necesitamos para dibujar el panel: nombres, O/C, naturaleza (alcista/bajista/neutral) y la hora de la última actualización para evitar trabajo redundante.
// Session data storage string g_CurrentSessionName = ""; double g_CurrentSessionOpen = 0; double g_CurrentSessionCurrent = 0; string g_CurrentSessionNature = ""; datetime g_CurrentSessionStart = 0; string g_PrevSessionName = ""; double g_PrevSessionOpen = 0; double g_PrevSessionClose = 0; string g_PrevSessionNature = ""; datetime g_LastSessionUpdate = 0;
6) Fondo de lienzo más ancho para adaptarse al nuevo panel
El bloque de información de la sesión añade tres líneas concisas para los resúmenes de la sesión actual y la anterior. Ajustamos ligeramente el ancho y la altura del panel para que el diseño respire y el texto no se ajuste de forma extraña.
// Canvas background - INCREASED WIDTH CCanvas g_bgCanvas; string g_bg_name = ""; int g_bg_x = 6; int g_bg_y = 44; // uses Y_OFFSET in OnInit int g_bg_w = 450; // increased from 430 int g_bg_h = 340; // increased from 300 for session info
7) Inicializa los nuevos controles en OnInit().
OnInit() es donde las entradas se convierten en estado activo. También preparamos el visualizador para que sepa qué ajustes de canal lateral se aplican a las sesiones (no a las principales): desplazamiento GMT, alternancia de mecha y alfa de mecha. Aunque la clase de sesión expone SetFillColors(), nuestros elementos visuales de sesión actuales utilizan rectángulos sin relleno, pero mantenemos la llamada por compatibilidad futura.
// Initialize session visualizer with separate session wick params g_sessions.SetGMTOffset(InpGMTOffset); g_sessions.SetFillColors(g_FillBull, g_FillBear); // kept for compatibility (no body fill in visualizer) g_sessions.SetShowWicks(g_ShowSessionWicks); // session-specific wick setting g_sessions.SetWickAlpha(g_SessionWickAlpha); // session-specific wick alpha // copy panel flag g_ShowSessionInfo = InpShowSessionInfo;
8) Agregar la interfaz de usuario de "Información de sesión"
Dibujamos pequeñas etiquetas dentro del panel de control para mostrar la sesión actual (nombre, abierta, naturaleza) y la sesión anterior (nombre, O/C, naturaleza). Los colores refuerzan sutilmente la imagen del toro y el oso. El panel se crea una sola vez; el contenido se actualiza en cada ciclo.
void UpdateSessionInfo() { if(!g_ShowSessionInfo) return; datetime now = TimeCurrent(); // Always refresh current session nature UpdateCurrentSessionInfo(now); // Update previous session only at boundaries if(g_LastSessionUpdate == 0 || IsNewSessionStart(now)) { UpdatePreviousSessionInfo(now); g_LastSessionUpdate = now; } UpdateSessionDisplay(); }
9) Contabilidad inteligente de sesiones (actual + anterior)
Dividimos las preocupaciones en tres tipos de ayuda:
- UpdateCurrentSessionInfo: averigua en qué sesión estamos (basada en GMT), almacena en caché el precio de apertura de la sesión y clasifica la naturaleza utilizando la oferta en tiempo real frente a la apertura de la sesión.
- UpdatePreviousSessionInfo: cuando se inicia una nueva sesión, se guarda una instantánea de la configuración y la naturaleza de la sesión anterior.
- UpdateSessionDisplay: actualiza los textos y colores más recientes en los objetos de etiqueta.
Está diseñado intencionadamente para ser ligero, sin bucles pesados, por lo que funciona bien con el ciclo de actualización HTF habitual de EA. Por el contrario, la versión anterior de EA no tenía ese concepto, razón por la cual la lógica de paneles y límites es completamente nueva en esta versión.
void UpdateSessionInfo() { if(!g_ShowSessionInfo) return; datetime now = TimeCurrent(); // Always refresh current session nature UpdateCurrentSessionInfo(now); // Update previous session only at boundaries if(g_LastSessionUpdate == 0 || IsNewSessionStart(now)) { UpdatePreviousSessionInfo(now); g_LastSessionUpdate = now; } UpdateSessionDisplay(); }
10) Controles de sesión en el panel de control
El poder debe estar al alcance de la mano del operador. Añadimos dos botones nuevos: uno para activar/desactivar las sesiones y otro para activar/desactivar las mechas de sesión. Mantenemos la independencia de los controles de los elementos principales Los botones se integran con los interruptores existentes para preservar la memoria muscular.
// NEW ROW: Session wicks, Sessions, Major VLines (swapped positions for clarity) CreateButton(btn_toggle_session_wicks, 12, 86 + Y_OFFSET, 130, 22, g_ShowSessionWicks ? "Sess Wicks: ON" : "Sess Wicks: OFF"); CreateButton(btn_toggle_sessions, 152, 86 + Y_OFFSET, 130, 22, g_ShowSessions ? "Sessions: ON" : "Sessions: OFF"); CreateButton(btn_toggle_maj_vlines, 292, 86 + Y_OFFSET, 130, 22, g_ShowMajorVLines ? "Maj VLines: ON" : "Maj VLines: OFF");
Y los controladores de clics simplemente invierten el bit y actualizan:
if(obj == btn_toggle_session_wicks) { g_ShowSessionWicks = !g_ShowSessionWicks; ObjectSetString(main_chart_id, btn_toggle_session_wicks, OBJPROP_TEXT, g_ShowSessionWicks ? "Sess Wicks: ON" : "Sess Wicks: OFF"); RefreshLines(); return; } if(obj == btn_toggle_sessions) { g_ShowSessions = !g_ShowSessions; ObjectSetString(main_chart_id, btn_toggle_sessions, OBJPROP_TEXT, g_ShowSessions ? "Sessions: ON" : "Sessions: OFF"); RefreshLines(); return; }
11) Dibujar sesiones dentro de RefreshLines() (un lugar para controlarlas todas)
La función principal de actualización de EA es RefreshLines(). Una vez que gestionamos los objetos HTF, le pedimos al visualizador que renderice las sesiones (con la configuración más reciente para cada sesión) o que las borre. Mantener esto aquí garantiza una única frecuencia de actualización y una sola pasada de limpieza.
// Draw sessions if toggled - NOW WITH SEPARATE WICK SETTINGS if(g_ShowSessions) { g_sessions.SetFillColors(g_FillBull, g_FillBear); // no-op for bodies today; future-proof g_sessions.SetShowWicks(g_ShowSessionWicks); g_sessions.SetWickAlpha(g_SessionWickAlpha); g_sessions.RefreshSessions(InpSessionLookback); } else { g_sessions.ClearAll(); }
En la versión anterior de EA, RefreshLines() solo gestionaba elementos visuales HTF/menores. Al centralizarse las sesiones aquí, un único temporizador/tick controla todos los elementos visuales, evitando parpadeos y conflictos de sincronización.
12) Actualización y limpieza en OnTick y OnDeinit
En cada ciclo recalculamos el estado de alto nivel: nuevas barras y ahora el bloque de información de la sesión. Durante la desinicialización también borramos los dibujos de la sesión para que la reconexión comience desde cero.
void OnTick() { bool need_refresh = false; // ... (bar-change checks for majors/minors) // Always update session info on tick for real-time current session nature UpdateSessionInfo(); if(need_refresh) RefreshLines(); } void DeleteAllHTFLines() { // remove HTF objects // ... // Also clear sessions g_sessions.ClearAll(); }
13) Panel más alto una vez restaurado, para que encaje en la nueva sección
Al minimizar/restaurar, la altura del fondo debe coincidir con la del contenido. Ajustamos la altura restaurada para que la información de la sesión sea completamente visible sin superponerse a otros elementos del gráfico.
void RestoreUI() { UpdateBackgroundHeight(340); // was 250; space for the Session Info CreateAllOtherUIObjects(); ObjectSetString(main_chart_id, btn_minimize, OBJPROP_TEXT, "Minimize"); }
Pruebas
Para esta prueba, adjuntamos el EA directamente a un gráfico en tiempo real (no al Probador de Estrategias), ya que nuestro enfoque se centra exclusivamente en los elementos visuales del gráfico. Esto nos permite verificar que la representación y los controles se ajustan a nuestros objetivos. A continuación se muestra la característica final en funcionamiento.

Figura 3: Resultado final con visualización de sesiones de Forex e información
Conclusión
Las sesiones del mercado Forex y de valores pueden proporcionar información más profunda a los traders y analistas financieros cuando se comprenden y visualizan adecuadamente. La idea que se implementa aquí nos ayuda a representar las sesiones de negociación como períodos de velas japonesas, revelando detalles valiosos sobre el máximo, el mínimo, la apertura y el cierre de cada sesión. Este enfoque nos permite aplicar nuestros conocimientos tradicionales sobre gráficos de velas japonesas al análisis a nivel de sesión, lo que ayuda a predecir posibles movimientos en la próxima sesión basándose en la naturaleza de las anteriores.
Mediante este desarrollo, introdujimos marcadores visuales y una utilidad de control dinámico (Sincronizador de períodos de mercado) que nos permite alternar, ajustar y sincronizar las visualizaciones de las sesiones directamente en el gráfico. Además, el panel de información de la sesión resume la apertura, el cierre y la dirección (alcista o bajista) de las sesiones actuales y anteriores, ofreciendo a los traders una visión rápida e intuitiva de las fuerzas subyacentes del mercado.
Dado que el carácter alcista o bajista de una sesión se mide a lo largo de períodos de tiempo prolongados, a menudo refleja un sesgo de mercado más amplio y una mayor tendencia direccional. Comprender estos comportamientos no solo agudiza la perspectiva técnica, sino que también fortalece la psicología del trading mediante la observación directa y visual de la dinámica del mercado.
Puedes experimentar, modificar y compartir tus ideas en los comentarios a continuación. Los archivos fuente adjuntos se encuentran disponibles para su consulta y para que pueda seguir explorando el tema.
Lecciones clave
| Lección clave: | Descripción: |
|---|---|
| Integración de clases y diseño modular | La creación de módulos reutilizables como CSessionVisualizer fomenta una arquitectura más limpia y un mantenimiento más sencillo. Al separar la lógica de visualización en una clase, EA gana flexibilidad: los desarrolladores pueden modificar los elementos visuales de la sesión o reutilizar el componente en otros proyectos sin tener que reescribir el código principal. |
| Variables de tiempo de ejecución y sincronización de la interfaz de usuario | Al reflejar los parámetros de entrada en variables globales de tiempo de ejecución, se garantiza que los controles de la interfaz de usuario en tiempo real (como deslizadores, botones e interruptores) puedan actualizar dinámicamente su comportamiento visual sin necesidad de reinicializar el EA. Este enfoque enseña cómo diseñar utilidades de gráficos responsivas utilizando interfaces de usuario basadas en objetos. |
| Gestión de sesiones y mapeo de datos | El uso de datos de sesión estructurados (por ejemplo, horas de inicio/fin basadas en GMT, cálculos de apertura/cierre) demuestra cómo conectar los datos de gráficos en tiempo real con la lógica de negociación basada en el tiempo. Refuerza la idea de que los desarrolladores pueden combinar análisis y elementos visuales para hacer que el contexto del mercado sea más intuitivo. |
Archivos adjuntos
| Nombre del archivo de origen | Versión | Descripción |
|---|---|---|
| SessionVisualizerTest.mqh | 1.0 | Un script de prueba independiente diseñado para verificar la representación y el comportamiento de la clase CSessionVisualizer. Permite a los desarrolladores aislar la lógica visual, garantizando que los rectángulos de sesión, los colores y las asignaciones de tiempo se muestren correctamente antes de la integración en sistemas más grandes. |
| SessionTest.mq5 | 1.0 | Un Asesor Experto simplificado diseñado específicamente para probar y demostrar la funcionalidad del encabezado SessionVisualizerTest. |
| SessionVisualizer.mqh | 1.0 | La implementación de la clase principal que gestiona la visualización de sesiones de Forex. Gestiona los ajustes de zona horaria, la asignación de colores y la representación gráfica de los límites de sesión en el gráfico, sirviendo como un módulo visual reutilizable para cualquier EA o indicador que necesite contexto basado en la sesión. |
| MarketPeriodsSychronizer_EA.mq5 | 1.02 | El Asesor Experto actualizado integra el Visualizador de Sesión y un nuevo panel de Información de Sesión. Sincroniza múltiples marcadores de marcos temporales, rellenos de sesión y análisis en tiempo real en un panel de control unificado, proporcionando a los traders una herramienta interactiva y educativa de visualización del período de mercado. |
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/20005
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Aprendizaje automático y Data Science (Parte 44): Pronóstico de series temporales OHLC de Forex mediante autorregresión vectorial (VAR)
Simulación de mercado: Position View (XV)
Del básico al intermedio: Clases (II)
Dominando los registros (Parte 8): Registros de errores con traducción automática
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso