English Русский Deutsch 日本語
preview
Introducción a MQL5 (Parte 23): Automatización de la estrategia de ruptura del rango de apertura (ORB)

Introducción a MQL5 (Parte 23): Automatización de la estrategia de ruptura del rango de apertura (ORB)

MetaTrader 5Trading |
143 6
ALGOYIN LTD
Israel Pelumi Abioye

Introducción

¡Bienvenidos de nuevo a la parte 23 de la serie Introducción a MQL5! En este artículo, le guiaré paso a paso para automatizar el método Opening Range Breakout (ORB) con MQL5. El objetivo es enseñarte MQL5 de forma práctica y accesible para principiantes, mediante proyectos, sin prometer beneficios ni respaldar ninguna estrategia de inversión. Trabajar con ejemplos reales como estos te ayudará a desarrollar tus habilidades en MQL5 de una manera útil, a la vez que te proporciona experiencia práctica con conceptos como el reconocimiento de rangos de ruptura, la ejecución de órdenes automatizadas y la gestión automatizada/programada de operaciones.

Este artículo explicará la estrategia Opening Range Breakout (ORB) y le mostrará cómo usar MQL5 para automatizarla. Aprenderás a establecer las condiciones de negociación, registrar el rango de apertura y configurar tu Asesor Experto para que realice operaciones automáticamente en respuesta a las rupturas. Para garantizar que las entradas solo se produzcan durante la ventana de mercado especificada, también analizaremos cómo emplear una lógica basada en el tiempo para regular cuándo se permite la ejecución de las operaciones. Al finalizar, sabrá exactamente cómo utilizar MetaTrader 5 para convertir esta estrategia de trading clásica en un sistema de trading totalmente automatizado.


Ruptura del rango de apertura (ORB)

La estrategia de ruptura del rango de apertura rastrea el máximo y el mínimo durante un breve período de tiempo inmediatamente posterior a la apertura del mercado. El rango de apertura se compone de ese valor alto y ese valor bajo. Después de eso, puedes buscar que el precio rompa por debajo del mínimo del rango o por encima del máximo del rango. Un posible impulso alcista se indica mediante una ruptura por encima del máximo, mientras que un posible impulso bajista se indica mediante una ruptura por debajo del mínimo. La idea es que la tendencia direccional para el resto del día suele estar determinada por la volatilidad de la sesión inicial.

Las duraciones típicas del rango de apertura son los primeros cinco, quince, treinta o sesenta minutos de la sesión. Los rangos más cortos proporcionan más señales, pero también más ruido, capturando una volatilidad extremadamente temprana. Aunque generan menos señales y son más estables, los rangos más largos suelen ser de mayor calidad. Elige un intervalo que se corresponda con el activo y el periodo de negociación. Puedes usar los primeros 15 o 30 minutos para estrategias de renta variable intradía.

Una regla de ruptura sencilla consiste en que el precio cierre por encima del máximo del rango inicial para una entrada en largo o por debajo del mínimo del rango de apertura para una entrada en corto. Algunos operadores necesitan más confirmación, como el cierre de una vela más un pequeño margen de pips para evitar rupturas falsas inducidas por el ruido. Otros esperan una nueva prueba, en la que el precio rompe el rango, regresa al límite y luego se mueve nuevamente hacia el punto de ruptura. Seleccione y realice pruebas retrospectivas con la regla de confirmación que mejor se adapte a su nivel de tolerancia al riesgo. Las rupturas de rango pueden implementarse colocando órdenes de stop en el límite del rango y dejando que el mercado las active, o entrando al mercado inmediatamente después de la primera ruptura confirmada.

Analogía

Consideremos un escenario en el que el mercado abre exactamente a las 9:30 de la mañana. La vela M15 que comienza a formarse en ese momento se convierte entonces en su principal foco de atención. Toma nota de los precios máximo y mínimo de la vela después de su cierre, y este se convierte en tu rango de apertura.

Después, espere pacientemente mientras cambia a un intervalo de tiempo más corto, como el gráfico de 5 minutos. Se indica una posible ruptura alcista (el precio sube) si posteriormente el precio supera el máximo de esa vela de 15 minutos. Se prevé una posible ruptura bajista (el precio podría seguir bajando) si el precio cae por debajo del mínimo.

En pocas palabras, se trata de establecer límites durante los primeros quince minutos después de la apertura del mercado y luego estar atento a la Ruptura del Rango de Apertura (ORB, por sus siglas en inglés), que ocurre cuando el precio rompe esos parámetros.

Figura 1. ORB


Cómo funciona el EA

Antes de comenzar la implementación programática, primero debe comprender el funcionamiento del Asesor Experto (EA) siguiendo su procedimiento detallado. El primer paso es determinar que el mercado abre a las 9:30 de la mañana (hora del servidor). El EA esperará atentamente la primera vela de 15 minutos que se forme exactamente a las 9:30. El asesor experto identificará el rango de apertura marcando y dibujando líneas en los precios máximos y mínimos de la vela una vez que esta cierre.

A continuación, el Asesor Experto copiará los datos de precios relevantes y comenzará a buscar señales de ruptura. Para iniciar una operación de compra, se espera a que una vela alcista cierre por encima del máximo del rango original. El stop loss (SL) se establece en el precio más bajo del rango, y el take profit se determina según la relación riesgo-recompensa (RRR) especificada por el usuario.

Figura 2. Lógica de compra

A continuación, el programa busca una vela bajista que cierre por debajo del mínimo de la misma vela de 15 minutos para iniciar una operación de venta. El TP volverá a calcularse según el RRR definido por el usuario, mientras que el SL se colocará en el máximo del rango. El número de posiciones que el EA debería abrir a la vez es otra opción de la que dispone el usuario. El EA debería dejar de operar después de que se rompa el rango, que es la primera señal de ruptura del día. 

Figura 3. Lógica de venta


Identificación de la hora de inicio del mercado

El siguiente paso es determinar la hora de apertura del mercado una vez que comprendamos a fondo cómo funciona el EA. A modo de ejemplo, supongamos que el mercado abre a las 9:30, hora del servidor. Requerimos que el EA detecte automáticamente ese momento en particular en todo momento. Si su EA se ejecuta en un VPS y no desea actualizar manualmente la fecha todos los días, necesita una forma de aislar solo la parte de la hora, ya que los valores de tiempo en MQL5 son muy precisos, puesto que incluyen el año, el mes, el día, la hora, el minuto y el segundo. De esta forma, el programa puede identificar automáticamente el día, el mes y el año de cada nueva sesión de negociación.

La dificultad radica en que, si bien la fecha subyacente varía, la misma hora y minuto se repiten a diario. Un valor de fecha y hora completo solo coincidirá una vez antes de fallar en días consecutivos si simplemente lo compara con un número fijo. Para solucionar esto, el EA debe distinguir entre los componentes de hora y fecha. Esto le permite determinar si la barra o el tick actual, independientemente del día, el mes o el año, coincide con la hora de inicio del mercado.

Ejemplo:

string open_time_string = "9:30";
datetime open_time;

ulong chart_id = ChartID();
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

   ObjectsDeleteAll(chart_id);

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

   open_time = StringToTime(open_time_string);

   Comment("OPEN TIME: ",open_time);

   ObjectCreate(chart_id,"OPEN TIME",OBJ_VLINE,0,open_time,0);
   ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_COLOR,clrBlue);
   ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_STYLE,STYLE_DASH);
   ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_WIDTH,2);

  }

Resultado:

Figura 4. Horario de apertura

Explicación:

La hora de inicio del mercado se almacena primero como una cadena simple en la línea string open_time_string = "9:30"; por ejemplo. Esto es simplemente una representación de la hora de apertura legible para humanos que usted desea que el EA reconozca. Posteriormente, este tiempo se almacenará en un formato que MQL5 pueda utilizar para cálculos y gráficos en la variable datetime open_time;.

El programa convierte la cadena en un valor de fecha y hora válido. Aunque la función datetime en MQL5 contiene la fecha y hora completas, cuando se utiliza una cadena como esta, la hora se establece automáticamente a las 9:30 del día actual. Esto garantiza que, sin necesidad de actualizar explícitamente la fecha, el EA pueda calcular la hora de apertura cada día.

A continuación, dibuja una línea vertical en el gráfico en el momento exacto en que abre el mercado. Esta línea vertical actúa como un marcador visual del inicio del rango de apertura.


Identificación del máximo y mínimo de la primera vela de 15 minutos

El siguiente paso consiste en copiar los datos de la primera vela de 15 minutos que se forma exactamente a las 9:30, hora del servidor. Estos dos precios son importantes porque determinan el rango de apertura del día. El mínimo de esta vela indica el nivel para una ruptura bajista, y su máximo indica el nivel en el que se verificaría una ruptura alcista. Establecemos el rango de referencia que el EA utilizará para decidir el punto de entrada a las operaciones durante la sesión matutina, determinando estos dos puntos cruciales.

Dado que establece el límite para las posibles fluctuaciones de precios, capturar el rango de apertura es esencial. Tras la fase inicial de consolidación, puedes utilizar estos niveles para determinar si el mercado va al alza o a la baja. Estos valores servirán como referencia en el EA; se puede generar una señal de compra si el precio sube por encima del máximo y se puede activar una señal de venta si cae por debajo del mínimo. Esto garantiza que las operaciones solo se realicen cuando el mercado muestre una ruptura clara con respecto al rango de apertura.

Ejemplo:
string open_time_string = "9:30";
datetime open_time;

string open_time_bar_close_string = "9:45";
datetime open_time_bar_close;

ulong chart_id = ChartID();

double m15_high[];
double m15_low[];
double m15_close[];
double m15_open[];
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

   ObjectsDeleteAll(chart_id);

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

   open_time = StringToTime(open_time_string);

   Comment("OPEN TIME: ",open_time);

   ObjectCreate(chart_id,"OPEN TIME",OBJ_VLINE,0,open_time,0);
   ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_COLOR,clrBlue);
   ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_STYLE,STYLE_DASH);
   ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_WIDTH,2);

   open_time_bar_close = StringToTime(open_time_bar_close_string);

   if(TimeCurrent() >= open_time_bar_close)
     {

      CopyHigh(_Symbol,PERIOD_M15,open_time,1,m15_high);
      CopyLow(_Symbol,PERIOD_M15,open_time,1,m15_low);
      CopyClose(_Symbol,PERIOD_M15,open_time,1,m15_close);
      CopyOpen(_Symbol,PERIOD_M15,open_time,1,m15_open);

      ObjectCreate(chart_id,"High",OBJ_TREND,0,open_time,m15_high[0],TimeCurrent(),m15_high[0]);
      ObjectSetInteger(chart_id,"High",OBJPROP_COLOR,clrBlue);
      ObjectSetInteger(chart_id,"High",OBJPROP_WIDTH,2);

      ObjectCreate(chart_id,"Low",OBJ_TREND,0,open_time,m15_low[0],TimeCurrent(),m15_low[0]);
      ObjectSetInteger(chart_id,"Low",OBJPROP_COLOR,clrBlue);
      ObjectSetInteger(chart_id,"Low",OBJPROP_WIDTH,2);

     }

  }

Resultado:

Figura 5. Máximo y mínimo del rango

Explicación:

Esta parte determina el intervalo completo de formación de la primera vela de 15 minutos, extrae su información de precios importante y transforma su tiempo de cierre a partir de una cadena de texto para que pueda utilizarse en los cálculos. El programa espera entonces a verificar la finalización de la vela y extrae de forma segura sus valores máximo, mínimo, de apertura y de cierre cuando la hora del servidor alcanza o supera esta hora de cierre.

El método de ruptura del rango de apertura depende de estos valores recuperados. El programa utilizará los precios máximos y mínimos, que delimitan el rango de apertura, para detectar condiciones de ruptura en marcos temporales más cortos. Para facilitar la visualización, también se añaden líneas al gráfico en estos niveles máximos y mínimos. Antes de realizar cualquier operación, esto le ayuda a asegurarse de que el EA está siguiendo los niveles correctos y le permite ver claramente el rango de apertura.

El programa muestra una línea vertical para marcar la hora exacta de apertura del mercado cada día, ya que la hora de apertura es una sola variable. A continuación, espera a que la vela M15 se cierre por completo antes de utilizar sus valores máximos y mínimos para garantizar la exactitud de los datos y evitar problemas de "matriz fuera de rango".


Uso de gráficos de 5 minutos para detectar rupturas del rango de apertura.

Una vez determinados los máximos y mínimos iniciales, el siguiente paso del programa es comprobar el marco temporal M5 en busca de rupturas por encima o por debajo del rango. Un cierre alcista por encima del máximo de M15 indica una posible compra, y un cierre bajista por debajo del mínimo indica una posible venta. Este análisis en un marco temporal inferior permite realizar entradas en el mercado de forma oportuna y precisa.

Ejemplo:
double m5_high[];
double m5_low[];
double m5_close[];
double m5_open[];
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

   ArraySetAsSeries(m5_high,true);
   ArraySetAsSeries(m5_low,true);
   ArraySetAsSeries(m5_close,true);
   ArraySetAsSeries(m5_open,true);

//---
   return(INIT_SUCCEEDED);
  }
if(TimeCurrent() >= open_time_bar_close)
  {

   CopyHigh(_Symbol,PERIOD_M15,open_time,1,m15_high);
   CopyLow(_Symbol,PERIOD_M15,open_time,1,m15_low);
   CopyClose(_Symbol,PERIOD_M15,open_time,1,m15_close);
   CopyOpen(_Symbol,PERIOD_M15,open_time,1,m15_open);

   ObjectCreate(chart_id,"High",OBJ_TREND,0,open_time,m15_high[0],TimeCurrent(),m15_high[0]);
   ObjectSetInteger(chart_id,"High",OBJPROP_COLOR,clrBlue);
   ObjectSetInteger(chart_id,"High",OBJPROP_WIDTH,2);

   ObjectCreate(chart_id,"Low",OBJ_TREND,0,open_time,m15_low[0],TimeCurrent(),m15_low[0]);
   ObjectSetInteger(chart_id,"Low",OBJPROP_COLOR,clrBlue);
   ObjectSetInteger(chart_id,"Low",OBJPROP_WIDTH,2);

  }

CopyHigh(_Symbol,PERIOD_M5,1,5,m5_high);
CopyLow(_Symbol,PERIOD_M5,1,5,m5_low);
CopyClose(_Symbol,PERIOD_M5,1,5,m5_close);
CopyOpen(_Symbol,PERIOD_M5,1,5,m5_open);

if(TimeCurrent() >= open_time_bar_close && m5_close[0] > m15_high[0] && m5_close[1] < m15_high[0])
  {

//BUY

  }

if(TimeCurrent() >= open_time_bar_close && m5_close[0] < m15_low[0] && m5_close[1] > m15_low[0])
  {

//SELL

  }

Explicación:

En esta sección del programa se declaran cuatro matrices. En estas matrices se almacenan los precios máximo, mínimo, de cierre y de apertura de las velas en el marco temporal M5. La vela más reciente está representada por el índice 0, y las velas anteriores están representadas por índices más altos. Cada matriz contiene una secuencia de datos de precios que se pueden recuperar mediante un índice.

Todas estas matrices se configuran como series temporales mediante la función ArraySetAsSeries() dentro del código de inicialización. De este modo, se garantiza que los datos más recientes aparezcan en el índice 0, seguidos de los datos más antiguos en orden ascendente. Dado que es coherente con la forma en que MetaTrader 5 organiza los datos de los gráficos, que coloca la barra más reciente al principio de la serie, configurar las matrices de esta manera es crucial.

Estos programas extraen del gráfico los precios máximo, mínimo, de apertura y de cierre, así como los datos de las velas de 5 minutos más recientes. Según los criterios establecidos, comenzando con la vela que se forma antes de la actual, el EA debería recopilar datos de las cinco velas de 5 minutos completadas anteriormente. El EA puede entonces rastrear el movimiento del precio e identificar si se produce una ruptura por encima o por debajo del rango de la primera vela de 15 minutos, almacenando estos datos en matrices para su posterior análisis.

Tras el cierre de la primera vela de quince minutos, esta lógica busca condiciones de ruptura. Si la vela más reciente del marco temporal inferior cerró por encima del máximo del rango de apertura de quince minutos, y la vela anterior de cinco minutos todavía estaba por debajo de ese máximo, entonces el primer criterio indica una ruptura alcista. Esto confirma una ruptura alcista del precio. Cuando la vela más reciente de cinco minutos cae por debajo del mínimo del rango de 15 minutos, después de que la vela anterior estuviera por encima de este, el segundo criterio identifica una ruptura bajista. Esto indica una posible oportunidad de venta al validar una caída.

Al examinar si la vela es alcista o bajista, este razonamiento proporciona un grado adicional de seguridad. Para generar una señal de compra, el programa se asegura de que la vela más reciente sea alcista, es decir, que haya terminado por encima de su precio de apertura, y que rompa y cierre por encima del máximo del rango. Esto ayuda a eliminar las falsas rupturas causadas por aumentos temporales de precios. Del mismo modo, cuando se genera una señal de venta, el programa verifica que la vela sea bajista, lo que significa que cerró por debajo de lo que abrió y cierra por debajo del mínimo del rango. Esto garantiza que la ruptura tenga un impulso real antes de que se inicie una operación.

Observará que el razonamiento es diferente del método más conocido basado en la apertura, ya que se refiere al cierre de la vela anterior en lugar de a la apertura de la actual. Esta comprobación basada en el cierre tiene la ventaja de verificar que el precio realmente superó el rango de apertura entre dos velas completadas. Se produjo una ruptura real cuando la vela más reciente cerró por encima del rango, mientras que la anterior cerró por debajo.

Por otro lado, cuando se producen brechas o aumentos abruptos de precios, basarse únicamente en la apertura de la vela puede resultar engañoso. La condición puede fallar incluso si el precio se ha movido realmente más allá del rango si la vela abre por encima de él debido a un repunte repentino, o puede producir una señal engañosa si la apertura fue provocada por un tick inusual. Estos problemas se evitan utilizando el precio de cierre anterior, que además ofrece una forma más precisa de identificar rupturas reales, especialmente en mercados volátiles.

Figura 6. Gap


Ejecución de operaciones

El siguiente paso es hacer que el programa ejecute operaciones basándose en la lógica de ruptura que hemos creado. Una vez que el Asesor Experto detecta una ruptura válida por encima o por debajo del rango de 15 minutos, debería abrir automáticamente una posición de compra o venta, respectivamente.

Ejemplo:
#include <Trade/Trade.mqh>
CTrade trade;
int MagicNumber = 533930;  // Unique Number
input double RRR= 2; // RRR
input double lot_size = 0.2;
double ask_price;
double take_profit;
datetime lastTradeBarTime = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

   ArraySetAsSeries(m5_high,true);
   ArraySetAsSeries(m5_low,true);
   ArraySetAsSeries(m5_close,true);
   ArraySetAsSeries(m5_open,true);

   trade.SetExpertMagicNumber(MagicNumber);

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

   ObjectsDeleteAll(chart_id);
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   open_time = StringToTime(open_time_string);

   Comment("OPEN TIME: ",open_time);

   ObjectCreate(chart_id,"OPEN TIME",OBJ_VLINE,0,open_time,0);
   ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_COLOR,clrBlue);
   ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_STYLE,STYLE_DASH);
   ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_WIDTH,2);

   open_time_bar_close = StringToTime(open_time_bar_close_string);

   if(TimeCurrent() >= open_time_bar_close)
     {

      CopyHigh(_Symbol,PERIOD_M15,open_time,1,m15_high);
      CopyLow(_Symbol,PERIOD_M15,open_time,1,m15_low);
      CopyClose(_Symbol,PERIOD_M15,open_time,1,m15_close);
      CopyOpen(_Symbol,PERIOD_M15,open_time,1,m15_open);

      ObjectCreate(chart_id,"High",OBJ_TREND,0,open_time,m15_high[0],TimeCurrent(),m15_high[0]);
      ObjectSetInteger(chart_id,"High",OBJPROP_COLOR,clrBlue);
      ObjectSetInteger(chart_id,"High",OBJPROP_WIDTH,2);

      ObjectCreate(chart_id,"Low",OBJ_TREND,0,open_time,m15_low[0],TimeCurrent(),m15_low[0]);
      ObjectSetInteger(chart_id,"Low",OBJPROP_COLOR,clrBlue);
      ObjectSetInteger(chart_id,"Low",OBJPROP_WIDTH,2);
     }

   CopyHigh(_Symbol,PERIOD_M5,1,5,m5_high);
   CopyLow(_Symbol,PERIOD_M5,1,5,m5_low);
   CopyClose(_Symbol,PERIOD_M5,1,5,m5_close);
   CopyOpen(_Symbol,PERIOD_M5,1,5,m5_open);

   datetime currentBarTime = iTime(_Symbol, PERIOD_M5, 0);
   ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);

   if(TimeCurrent() >= open_time_bar_close && m5_close[0] > m15_high[0] && m5_close[1] < m15_high[0] && m5_close[0] > m5_open[0] && currentBarTime != lastTradeBarTime)
     {

      //BUY
      take_profit = MathAbs(ask_price + ((ask_price - m15_low[0]) * RRR));
      trade.Buy(lot_size,_Symbol,ask_price,m15_low[0],take_profit);
      lastTradeBarTime = currentBarTime;

     }

   if(TimeCurrent() >= open_time_bar_close && m5_close[0] < m15_low[0] && m5_close[1] > m15_low[0] && m5_close[0] < m5_open[0] && currentBarTime != lastTradeBarTime)
     {

      //SELL
      take_profit = MathAbs(ask_price - ((m15_high[0] - ask_price) * RRR));
      trade.Sell(lot_size,_Symbol,ask_price,m15_high[0],take_profit);
      lastTradeBarTime = currentBarTime;
     }
  }

Figura 7. Múltiples rupturas

Explicación:

El software puede utilizar las rutinas de negociación integradas para abrir, modificar y cerrar órdenes mediante la importación de la biblioteca de negociación. Todas las acciones relacionadas con las operaciones son gestionadas por un objeto de trading. Un MagicNumber es un número de identificación especial que cada Asesor Experto utiliza para identificar y controlar sus propias operaciones, incluso cuando varios sistemas automatizados operan en la misma cuenta.

Los parámetros definidos por el usuario son lot_size y RRR (Relación Riesgo-Recompensa). Lot_size regula el volumen de la transacción, y RRR establece la cantidad de veces que el take profit debe superar el stop loss. Al registrar el precio ask actual, el nivel de take profit calculado y la hora de la transacción más reciente para evitar múltiples entradas en la misma vela, las variables ask_price, take_profit y lastTradeBarTime ayudan a gestionar la ejecución de las operaciones.

Para garantizar que el número de identificación único del EA se incluya en cada operación que abre, se utiliza un comando. Mientras que una variable registra el precio ask actual del mercado, otra recoge la hora de apertura de la vela actual. Sin embargo, se vuelve problemático cuando el EA realiza varias operaciones de ruptura en una sola sesión, lo que puede ser arriesgado en un mercado errático. La exposición al riesgo aumenta al realizar múltiples operaciones de ruptura, ya que el stop loss suele estar lejos del punto de entrada. Lo ideal es que el Asesor Experto (EA) esté configurado para ejecutar únicamente la primera operación de ruptura legítima del día, con el fin de reducir el riesgo y evitar el exceso de operaciones.

Para lograr esto, debemos modificar el programa para que busque la primera ruptura y luego deje de aceptar operaciones durante el resto de la sesión una vez completada dicha operación. De este modo, el Asesor Experto (EA) tiene la garantía de mantener la disciplina y concentrarse únicamente en la primera oportunidad de ruptura, en lugar de reaccionar a cada posible movimiento en falso que pueda surgir más adelante durante el día.

Ejemplo:
int open_to_current;
bool isBreakout = false;
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

   open_time = StringToTime(open_time_string);

   ObjectCreate(chart_id,"OPEN TIME",OBJ_VLINE,0,open_time,0);
   ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_COLOR,clrBlue);
   ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_STYLE,STYLE_DASH);
   ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_WIDTH,2);

   open_time_bar_close = StringToTime(open_time_bar_close_string);

   if(TimeCurrent() >= open_time_bar_close)
     {

      CopyHigh(_Symbol,PERIOD_M15,open_time,1,m15_high);
      CopyLow(_Symbol,PERIOD_M15,open_time,1,m15_low);
      CopyClose(_Symbol,PERIOD_M15,open_time,1,m15_close);
      CopyOpen(_Symbol,PERIOD_M15,open_time,1,m15_open);

      ObjectCreate(chart_id,"High",OBJ_TREND,0,open_time,m15_high[0],TimeCurrent(),m15_high[0]);
      ObjectSetInteger(chart_id,"High",OBJPROP_COLOR,clrBlue);
      ObjectSetInteger(chart_id,"High",OBJPROP_WIDTH,2);

      ObjectCreate(chart_id,"Low",OBJ_TREND,0,open_time,m15_low[0],TimeCurrent(),m15_low[0]);
      ObjectSetInteger(chart_id,"Low",OBJPROP_COLOR,clrBlue);
      ObjectSetInteger(chart_id,"Low",OBJPROP_WIDTH,2);

      open_to_current = Bars(_Symbol,PERIOD_M5,open_time_bar_close,TimeCurrent());

      CopyHigh(_Symbol,PERIOD_M5,1,open_to_current,m5_high);
      CopyLow(_Symbol,PERIOD_M5,1,open_to_current,m5_low);
      CopyClose(_Symbol,PERIOD_M5,1,open_to_current,m5_close);
      CopyOpen(_Symbol,PERIOD_M5,1,open_to_current,m5_open);

     }

   datetime currentBarTime = iTime(_Symbol, PERIOD_M5, 0);
   ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);

   if(TimeCurrent() >= open_time_bar_close && m5_close[0] > m15_high[0] && m5_close[1] < m15_high[0] && m5_close[0] > m5_open[0] && currentBarTime != lastTradeBarTime && isBreakout == false)
     {

      //BUY
      take_profit = MathAbs(ask_price + ((ask_price - m15_low[0]) * RRR));
      trade.Buy(lot_size,_Symbol,ask_price,m15_low[0],take_profit);
      lastTradeBarTime = currentBarTime;

     }

   if(TimeCurrent() >= open_time_bar_close && m5_close[0] < m15_low[0] && m5_close[1] > m15_low[0] && m5_close[0] < m5_open[0] && currentBarTime != lastTradeBarTime && isBreakout == false)
     {

      //SELL
      take_profit = MathAbs(ask_price - ((m15_high[0] - ask_price) * RRR));
      trade.Sell(lot_size,_Symbol,ask_price,m15_high[0],take_profit);
      lastTradeBarTime = currentBarTime;

     }

   if(TimeCurrent() >= open_time_bar_close)
     {
      for(int i = 0; i < open_to_current; i++)
        {
         if(i + 1  < open_to_current)
           {
            if((m5_close[i] > m15_high[0] && m5_close[i + 1] < m15_high[0]) || (m5_close[i] < m15_low[0] && m5_close[i + 1] > m15_low[0]))
              {
               isBreakout = true;
               break;
              }
           }
        }
     }

   if(TimeCurrent() < open_time)
     {
      isBreakout = false;
     }

   Comment(isBreakout);

  }

Resultado:

Figura 8. Ruptura única

Explicación:

Se añadieron dos nuevas variables: una para determinar el número de velas que se han formado desde el final de la barra de quince minutos y otra para controlar la aparición de una ruptura. El programa comprueba continuamente los datos de las velas hasta la hora actual utilizando una variable dinámica para detectar rupturas.

Para que el EA ejecute solo una operación por sesión, la variable isBreakout actúa como un indicador. Cuando se establece inicialmente en falso, el sistema tiene libertad para ejecutar una operación de ruptura. Cuando se identifica una ruptura legítima, ya sea alcista o bajista, el código dentro del bucle establece isBreakout en verdadero, lo que impide que se realicen más operaciones. Esto ayuda a mantener la regla de que el EA solo debe ejecutar una ruptura por día.

El programa ahora recopila todos los datos disponibles de las velas desde el final del rango de apertura hasta el presente mediante una variable dinámica. Esta mejora aumenta la precisión y la eficiencia de la detección de rupturas, ya que permite monitorizar continuamente las nuevas velas a medida que se van formando.

Finalmente, la sección if(TimeCurrent() < open_time) restablece isBreakout a falso antes de que comience el nuevo día de negociación, por lo que el EA estará listo para detectar y operar la ruptura del día siguiente.

Es posible que desee permitir que el software abra numerosas posiciones durante una sola ruptura, ya que está diseñado para ejecutar únicamente una operación de ruptura al día. El término "múltiples entradas" se utiliza comúnmente para describir esta idea. Cuando se produce una ruptura, el EA puede abrir un número de operaciones especificado por el usuario a la vez, en lugar de simplemente una. 

Ejemplo:

#include <Trade/Trade.mqh>
CTrade trade;
int MagicNumber = 533930;  // Unique Number
input double RRR= 2; // RRR
input double lot_size = 0.2;
input int pos_num = 2; // Number of Positions to Open 
if(TimeCurrent() >= open_time_bar_close && TimeCurrent() <= close_time && m5_close[0] > m15_high[0] && m5_close[1] < m15_high[0] && m5_close[0] > m5_open[0] && currentBarTime != lastTradeBarTime && isBreakout == false)
  {

//BUY
   take_profit = MathAbs(ask_price + ((ask_price - m15_low[0]) * RRR));

   for(int i = 0; i < pos_num; i++)  // open 3 trades
     {
      trade.Buy(lot_size,_Symbol,ask_price,m15_low[0],take_profit);
     }
   lastTradeBarTime = currentBarTime;
  }

if(TimeCurrent() >= open_time_bar_close && m5_close[0] < m15_low[0] && m5_close[1] > m15_low[0] && m5_close[0] < m5_open[0] && currentBarTime != lastTradeBarTime && isBreakout == false)
  {

//SELL
   take_profit = MathAbs(ask_price - ((m15_high[0] - ask_price) * RRR));

   for(int i = 0; i < pos_num; i++)  // open 3 trades
     {
      trade.Sell(lot_size,_Symbol,ask_price,m15_high[0],take_profit);
     }

   lastTradeBarTime = currentBarTime;
  }

if(TimeCurrent() >= open_time_bar_close)
  {
   for(int i = 0; i < open_to_current; i++)
     {
      if(i + 1  < open_to_current)
        {
         if((m5_close[i] > m15_high[0] && m5_close[i + 1] < m15_high[0]) || (m5_close[i] < m15_low[0] && m5_close[i + 1] > m15_low[0]))
           {
            isBreakout = true;
            break;
           }
        }
     }
  }

Explicación:

Para permitir a los operadores seleccionar cuántas posiciones debe abrir el programa tras una ruptura válida, se ha añadido un campo de entrada definido por el usuario. Antes de que se lance el EA, este valor se puede cambiar en la configuración de entrada. Por ejemplo, cuando se detecta una condición de ruptura, el EA abrirá automáticamente dos posiciones distintas si el número está configurado en 2.

El programa utiliza un bucle para abrir varias operaciones según el número especificado por el usuario. Dependiendo del valor de entrada, el bucle se repite, abriendo una nueva operación con los mismos criterios de entrada, límite de pérdidas y parámetros de toma de ganancias en cada ocasión. Por ejemplo, después de que se confirme una ruptura legítima, el software ejecutará dos operaciones seguidas si el número está configurado en 2.

Además, queremos confirmar que el EA solo puede realizar operaciones antes de las 15:00, hora del servidor. De este modo, se preserva la disciplina en las operaciones y se evita que el sistema realice transacciones durante períodos de menor actividad, cuando la volatilidad y la liquidez pueden haber disminuido. El EA debería dejar de realizar operaciones cuando el reloj marque las quince en punto. Además, el EA debería cerrar inmediatamente cualquier transacción abierta que aún esté activa en ese momento. Esto mantiene la coherencia del enfoque y reduce la exposición innecesaria al riesgo, al garantizar que cada operación comercial finalice dentro de la sesión activa.

 Ejemplo:

open_time = StringToTime(open_time_string);
close_time = StringToTime(close_time_string);
ObjectCreate(chart_id,"OPEN TIME",OBJ_VLINE,0,open_time,0);
ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_COLOR,clrBlue);
ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_STYLE,STYLE_DASH);
ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_WIDTH,2);

ObjectCreate(chart_id,"CLOSE TIME",OBJ_VLINE,0,close_time,0);
ObjectSetInteger(chart_id,"CLOSE TIME",OBJPROP_COLOR,clrRed);
ObjectSetInteger(chart_id,"CLOSE TIME",OBJPROP_STYLE,STYLE_DASH);
ObjectSetInteger(chart_id,"CLOSE TIME",OBJPROP_WIDTH,2);
if(TimeCurrent() >= open_time_bar_close && TimeCurrent() <= close_time && m5_close[0] > m15_high[0] && m5_close[1] < m15_high[0] && m5_close[0] > m5_open[0] && currentBarTime != lastTradeBarTime && isBreakout == false)
  {

//BUY
   take_profit = MathAbs(ask_price + ((ask_price - m15_low[0]) * RRR));

   for(int i = 0; i < pos_num; i++)  // open 3 trades
     {
      trade.Buy(lot_size,_Symbol,ask_price,m15_low[0],take_profit);
     }
   lastTradeBarTime = currentBarTime;
  }

if(TimeCurrent() >= open_time_bar_close && TimeCurrent() <= close_time && m5_close[0] < m15_low[0] && m5_close[1] > m15_low[0] && m5_close[0] < m5_open[0] && currentBarTime != lastTradeBarTime && isBreakout == false)
  {

//SELL
   take_profit = MathAbs(ask_price - ((m15_high[0] - ask_price) * RRR));

   for(int i = 0; i < pos_num; i++)  // open 3 trades
     {
      trade.Sell(lot_size,_Symbol,ask_price,m15_high[0],take_profit);
     }

   lastTradeBarTime = currentBarTime;
  }

if(TimeCurrent() >= open_time_bar_close)
  {
   for(int i = 0; i < open_to_current; i++)
     {
      if(i + 1  < open_to_current)
        {
         if((m5_close[i] > m15_high[0] && m5_close[i + 1] < m15_high[0]) || (m5_close[i] < m15_low[0] && m5_close[i + 1] > m15_low[0]))
           {
            isBreakout = true;
            break;
           }
        }
     }
  }

if(TimeCurrent() < open_time)
  {
   isBreakout = false;
  }

// Comment(isBreakout);

for(int i = 0; i < PositionsTotal(); i++)
  {
   ulong ticket = PositionGetTicket(i);

   if(PositionGetInteger(POSITION_MAGIC) == MagicNumber  && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id))
     {

      if(TimeCurrent() >= close_time)
        {
         // Close the position
         trade.PositionClose(ticket);
        }
     }
  }

Resultado:

Figura 9. Horario de apertura y cierre

Explicación:

El programa define primero la hora límite de negociación deseada, que indica cuándo el EA debe dejar de aceptar nuevas operaciones, como un valor de texto. Posteriormente, genera una variable para almacenar este tiempo de forma adecuada, de manera que el sistema pueda comprenderlo y utilizarlo para comparaciones de tiempo basadas en la ejecución. A continuación, la aplicación transforma el texto de la hora de cierre proporcionada en un valor en tiempo real que puede identificar y comparar con la hora del mercado en ese momento. Además, se añade una línea discontinua roja vertical a la pantalla para representar visualmente el momento preciso en el que debe cesar la negociación. Esto facilita a los operadores la visualización del punto límite para la ejecución de operaciones en el gráfico.

Este requisito garantiza que el EA solo pueda realizar nuevas operaciones antes de la hora límite establecida. El programa detiene automáticamente la apertura de nuevas posiciones cuando el tiempo actual supera el límite especificado, impidiendo que se realicen operaciones fuera del período permitido. Finalmente, todas las operaciones activas son analizadas por la sección encargada de buscar posiciones abiertas. Al comparar el número de identificación y el símbolo distintivos, se confirma que todas las posiciones pertenecen a este EA. El Asesor Experto cierra automáticamente todas las operaciones activas cuando la hora actual alcanza o supera el límite establecido.

Nota:

La estrategia de este artículo se basa completamente en proyectos y tiene como objetivo enseñar a los lectores MQL5 a través de aplicaciones prácticas en el mundo real. No es un método garantizado para obtener beneficios en el trading en tiempo real.

 

Conclusión

En este artículo analizamos la automatización del método Opening Range Breakout (ORB) con MQL5. Hemos creado un Asesor Experto (EA) que reconoce las rupturas entre varios marcos temporales y realiza operaciones automáticamente de acuerdo con directrices preestablecidas. Además, diseñamos una ventana de negociación específica para detener las operaciones después de las 15:00 (hora del servidor) e implementamos limitaciones comerciales para limitar el número de entradas por ruptura. Para garantizar una ejecución disciplinada y una gestión de riesgos eficiente, el EA también cierra todas las posiciones abiertas una vez finalizado el período de negociación. Este enfoque metódico permite construir sistemas de negociación automatizados más sofisticados y fiables.

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

Archivos adjuntos |
Mustafa Nail Sertoglu
Mustafa Nail Sertoglu | 15 oct 2025 en 14:16
Mustafa Nail Sertoglu #:
Gracias por compartir tus ideas de trading con ORB, pero por favor comprueba tus condiciones de VENTA/COMPRA, ya que parece que están INVERTIDAS o son contrarias a tus definiciones (yo también lo volveré a comprobar).
¿Quizás la señal de COMPRA «NO cumple TODAS LAS CONDICIONES», incluso si el precio de oferta/demanda xauusd: COMPRA por encima de ORB superen la línea superior del ORB ?
ALGOYIN LTD
Israel Pelumi Abioye | 15 oct 2025 en 14:31
Mustafa Nail Sertoglu #:
Gracias por compartir tus ideas de trading con ORB, pero por favor comprueba tus condiciones de VENTA/COMPRA, ya que parece que están INVERTIDAS o son contrarias a tus definiciones (yo también lo volveré a comprobar).
Hola, gracias por tus amables palabras. Las condiciones son exactamente como se explican en el artículo, no hay ningún error.
Daniel Cuyabre burgoa
Daniel Cuyabre burgoa | 7 jul 2026 en 01:17
Gracias por la explicación sobre la apertura o la ruptura que hay en la vela de acuerdo con la hora de las 9:30 de la mañana esa información es estratégica.
ALGOYIN LTD
Israel Pelumi Abioye | 7 jul 2026 en 08:22
Daniel Cuyabre burgoa #:
Gracias por explicarme la brecha o ruptura en la vela de las 9:30 h; esa información es fundamental.
De nada.
Michael Charles Schefe
Michael Charles Schefe | 7 jul 2026 en 08:35
Mustafa Nail Sertoglu #:
¿Quizás la señal de COMPRA «NO cumple TODAS LAS CONDICIONES» aunque el precio de oferta/demanda esté por encima de la línea superior del ORB?
Israel Pelumi Abioye n. º:
Hola, gracias por tus amables palabras. Las condiciones son exactamente como se explican en el artículo, sin errores.
En realidad, el autor ha indicado el precio de venta para abrir tanto operaciones de compra como de venta. Nosotros siempre abrimos operaciones de venta al precio de compra, pero el autor ha puesto el precio de venta.
Características del Wizard MQL5 que debe conocer (Parte 69): Uso de patrones del SAR y del RVI Características del Wizard MQL5 que debe conocer (Parte 69): Uso de patrones del SAR y del RVI
El Parabolic SAR (SAR) y el Relative Vigor Index (RVI) son otro par de indicadores que podrían utilizarse conjuntamente en un asesor experto de MQL5. Este par de indicadores, al igual que los que hemos analizado anteriormente, también es complementario, ya que el SAR define la tendencia mientras que el RVI evalúa el impulso. Como de costumbre, utilizamos el asistente MQL5 para construir y probar cualquier potencial que pueda tener este emparejamiento de indicadores.
Aprendizaje automático y Data Science (Parte 43): Detección de patrones ocultos en datos de indicadores con modelos de mezcla gaussiana latente (LGMM) Aprendizaje automático y Data Science (Parte 43): Detección de patrones ocultos en datos de indicadores con modelos de mezcla gaussiana latente (LGMM)
¿Alguna vez has mirado un gráfico y has tenido esa extraña sensación... de que hay un patrón oculto justo debajo de la superficie? ¿Un código secreto que podría revelar hacia dónde se dirigen los precios si tan solo pudieras descifrarlo? Les presentamos LGMM, el detector de patrones ocultos del mercado. Un modelo de aprendizaje automático que ayuda a identificar esos patrones ocultos en el mercado.
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.
Motor de decisión Multi-IA para MQL5 (Parte 4): ¿El multi-IA le gana a una sola IA? Diario, scorecard y costo Motor de decisión Multi-IA para MQL5 (Parte 4): ¿El multi-IA le gana a una sola IA? Diario, scorecard y costo
Esta parte añade la capa de evaluación al motor multi-IA. Cada ciclo queda registrado en un diario con la señal y la confianza de cada proveedor y el voto combinado; al vencer el horizonte, se califica a cada IA como si la siguiéramos sola y se compara en igualdad de condiciones. Además, se estima el costo de las llamadas y se muestra un cuadro de mando en el gráfico y un CSV para decidir si el conjunto aporta y qué modelo conviene podar o ajustar.