English Русский 中文 Deutsch 日本語 Português
preview
Guía paso a paso para operar con la estrategia de ruptura de estructura (BoS, Break of Structure)

Guía paso a paso para operar con la estrategia de ruptura de estructura (BoS, Break of Structure)

MetaTrader 5Trading | 16 octubre 2024, 09:54
1 466 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Introducción

En este artículo, analizaremos la Ruptura de Estructura (BoS, Break of Structure), un término que significa un cambio significativo en la tendencia o dirección del mercado, la estrategia de trading de divisas, en el contexto del Concepto de Dinero Inteligente (SMC, Smart Money Concept) y la creación de un Asesor Experto (EA, Expert Advisord) basado en él.

Vamos a explorar la definición, los tipos, las aplicaciones de la estrategia comercial y el desarrollo en MetaQuotes Language 5 (MQL5) para MetaTrader 5 (MT5) a medida que profundizamos en los matices de la Ruptura de la Estructura. La noción de ruptura de la estructura es una herramienta útil que los operadores deben aprender para aumentar su capacidad de predecir los movimientos del mercado, tomar mejores decisiones y, finalmente, dominar la gestión del riesgo. Utilizando los siguientes temas, lograremos lo anterior:

  1. Definición de ruptura de estructura (BoS)
  2. Descripción de la ruptura de la estructura (BoS)
  3. Tipos de rupturas de estructura (BoS)
  4. Descripción de la estrategia de negociación
  5. Plano de estrategia de negociación
  6. Implementación en MetaQuotes Language 5 (MQL5)
  7. Resultados del probador de estrategias
  8. Conclusión

En este viaje, utilizaremos ampliamente MetaQuotes Language 5 (MQL5) como nuestro entorno de codificación IDE base, y ejecutaremos los archivos en MetaTrader 5 (MT5) terminal de trading. Por lo tanto, disponer de las versiones mencionadas será de vital importancia. Comencemos pues.


Definición de ruptura de estructura (BoS)

La ruptura de estructura (BoS) es un concepto clave en el análisis técnico que utiliza conceptos de dinero inteligente (SMC) para identificar cambios significativos en las tendencias o direcciones del mercado. Generalmente ocurre cuando el precio se mueve decisivamente a través de mínimos o máximos que habían sido establecidos por la acción del precio anterior. Cuando los precios suben por encima de los máximos oscilantes o caen por debajo de los mínimos oscilantes, simplemente rompen la estructura de mercado previamente formada y de ahí su nombre “Ruptura” de estructura. Esto generalmente indica un cambio en el sentimiento del mercado y la dirección de la tendencia, señalando una continuación de la tendencia existente o el comienzo de una nueva tendencia.


Descripción de la ruptura de la estructura (BoS)

Para describir eficazmente una Ruptura de Estructura (BoS), primero distingámosla de los otros elementos del Concepto de Dinero Inteligente, que son el Cambio de Estructura del Mercado (MSS, Market Structure Shift) y el Cambio de Carácter (CHoCH, Change of Character).

  • Cambio de la estructura del mercado (MSS, Market Structure Shift)

El cambio de la estructura del mercado, que quizás también hayas oído llamar cambio de impulso del mercado (MMS), ocurre debido a una ruptura de precio en el máximo más reciente con respecto a una tendencia bajista o, inversamente, el máximo más reciente con respecto a una tendencia alcista sin romper primero el mínimo o máximo oscilatorio más reciente respectivamente. Esto significa una reversión de tendencia debido al cambio de estructura, de ahí su nombre “cambio” en la estructura del mercado.

Cambio de la estructura del mercado

  • Cambio de carácter (CHoCH, Change of Character)

El cambio de carácter, por otro lado, ocurre debido a la ruptura del precio del máximo más reciente en una tendencia bajista después de romper primero el mínimo más reciente, o debido a la ruptura del precio del mínimo más reciente en una tendencia alcista, después de romper primero el máximo más reciente.

Cambio de carácter

  • Ruptura de la estructura (BoS)

Ahora que conocemos las diferencias clave entre los tres elementos principales del enfoque del Concepto de Dinero Inteligente basado en la estructura del mercado, profundicemos en el tema principal del artículo, que es su ruptura. De la definición anterior proporcionada, deberías haber notado que una ruptura de estructura significa romper viejos máximos o mínimos para crear nuevos máximos o mínimos respectivamente. Cada instancia de una ruptura de estructura ayuda a que el mercado tienda hacia arriba, creando un nuevo máximo más alto (HH, Higher High) y un nuevo mínimo más alto (HL, Higher Low), o hacia abajo, creando un nuevo máximo más bajo (LH, Lower High) y un nuevo mínimo más bajo (LL, Lower Low), generalmente descritos como los puntos máximo y mínimo del precio.

Ruptura de la estructura

Sólo prevalece una regla: la ruptura debe ser con el cierre de la vela. Esto significa que en el caso de una ruptura con respecto al máximo, el precio de cierre debería estar por encima del punto de oscilación, mientras que en el caso de una ruptura con respecto al mínimo, el precio de cierre también debería estar por debajo del punto de oscilación. En pocas palabras, solo las rupturas en los cuerpos de las velas o barras se consideran rupturas de estructuras válidas, lo que significa que las rupturas en las colas, sombras o mechas de las velas se consideran rupturas de estructuras no válidas.

  • Configuraciones de BoS no válidas:

BoS no válido

  • Configuraciones de BoS válidas:

BoS válido


Tipos de rupturas de estructura

Como ya se ha dicho, las rupturas de estructuras se producen en mercados con tendencia, lo que significa que ocurren tanto en tendencias alcistas como bajistas. Esto ya sugiere que sólo tenemos dos tipos de rupturas de estructuras.

  • Ruptura alcista de la estructura

Estos ocurren en tendencias alcistas que se caracterizan por máximos más altos (HH) y mínimos más altos (HL). Técnicamente, una ruptura de estructura resulta del hecho de que el precio rompe el máximo más alto reciente en la tendencia alcista y forma un nuevo máximo más alto.

Toro BoS

  • Ruptura bajista de la estructura

Aquí, la ruptura bajista de la estructura se produce en tendencias bajistas, compuestas por mínimos más bajos (LL) y máximos más bajos (LH). Una ruptura de estructura resulta del hecho de que el precio rompe el mínimo más bajo reciente en la tendencia bajista y forma un nuevo mínimo más bajo.

Oso BoS


Descripción de la estrategia de negociación

Para operar eficazmente utilizando esta estrategia es necesario seguir una serie de pasos, pero no te preocupes. Los cubriremos paso a paso. 

Base en marcos temporales superiores (HTF, Higher Time Frames): en primer lugar, para un análisis exhaustivo, examine los marcos temporales superiores para un activo seleccionado, ya que proporciona una descripción general de las tendencias del mercado. Esto puede incluir un marco de tiempo de cuatro horas o diario, ya que tienden a revelar la trayectoria a largo plazo del mercado. Evitamos utilizar un marco temporal inferior ya que contiene muchos puntos de oscilación debido a las manipulaciones, barridos de liquidez y zigzags como un conductor mareado, lo que resulta en rupturas más insignificantes.

Identifique la tendencia subyacente del mercado: en segundo lugar, debe identificar la tendencia actual del mercado en su gráfico. Las tendencias alcistas contienen patrones de máximos y mínimos más altos en la acción del precio, mientras que las tendencias bajistas consisten en patrones de mínimos y máximos más bajos.

Identificar los puntos de entrada: después de identificar la tendencia actual en un marco temporal superior, puede infiltrarse en el mercado en la ruptura de un máximo o mínimo oscilante que cierre con el cuerpo de la vela que se rompe. Cuanto más fuerte sea la vela, más tranquilizadora será la confirmación de la señal.

Ejemplo de tendencia alcista:

Ejemplo de tendencia alcista

Ejemplo de tendencia bajista:

Ejemplo de tendencia bajista

En un marco de tiempo más pequeño, como un marco de tiempo de cinco minutos, puede utilizar estrategias de confirmación adicionales como oferta y demanda, indicadores técnicos como el Índice de Fuerza Relativa (RSI, Relative Strength Index) y/o Divergencia de convergencia de media móvil (MACD, Moving Average Convergence Divergence) o patrones de velas japonesas como patrones envolventes o de barra interna.

Identificar puntos de salida: Al entrar al mercado, necesitamos una estrategia sólida también para salir del mercado gestionando nuestros riesgos. Para el stop loss, lo colocamos en el punto de giro anterior, siempre que esté cerca del punto de entrada de la posición y al mismo tiempo nos deje una ganancia significativa. Si ese no es el caso, utilizamos una relación riesgo-recompensa de pips fijos. Por el contrario, tomamos ganancias en el siguiente punto de giro, pero como es difícil determinar el futuro punto de giro para el nivel de toma de ganancias, utilizamos la relación riesgo-recompensa como un faro para la toma de ganancias.

Plano de estrategia de negociación

Para comprender fácilmente el concepto que hemos transmitido, visualicémoslo en un plano.

Ruptura alcista de la estructura:

Plan de acción de Toro BoS

Ruptura bajista de la estructura:

Plano del Oso BoS


Implementación en lenguaje MetaQuotes 5 (MQL5) para MetaTrader 5 (MT5)

Después de aprender todas las teorías sobre la estrategia comercial de ruptura de estructura, automaticemos la teoría y creemos un asesor experto (EA) en lenguaje MetaQuotes 5 (MQL5) para MetaTrader 5.

Para crear un EA, en su terminal MetaTrader 5, haga clic en la pestaña Herramientas y marque MetaQuotes Language Editor, o simplemente pulse F4 en su teclado. Esto abrirá el entorno del Editor de Lenguajes de MetaQuotes, que permite escribir robots comerciales, indicadores técnicos, scripts y bibliotecas de funciones.

MetaQuotes abierto

Una vez abierto el MetaEditor, haga clic en Nuevo y, en el asistente que aparece, marque Asesor Experto (plantilla) y haga clic en Siguiente.

Creando un nuevo archivo EA

Dar nombre al archivo

Luego proporcione el nombre de archivo del asesor experto que desee, haga clic en Siguiente, luego en Siguiente y luego en Finalizar. Después de hacer todo eso, ahora estamos listos para codificar y programar nuestra estrategia de Ruptura de Estructura (BoS).

Primero, incluimos una instancia comercial mediante el uso #include al principio del código fuente. Esto nos da acceso a la clase CTrade que usaremos para crear un objeto comercial. Esto es crucial porque lo necesitamos para abrir operaciones.

#include <Trade/Trade.mqh>
CTrade obj_Trade;

La mayoría de nuestras actividades se ejecutarán en el manejador de eventos OnTick. Dado que se trata de una acción de precio pura, no necesitaremos utilizar el manejador de eventos OnInit para la inicialización del manejador del indicador. Así, todo nuestro código se ejecutará únicamente en el manejador de eventos OnTick. En primer lugar, echemos un vistazo a los parámetros que toma la función además de sus funciones, ya que es el corazón de este código:

void OnTick(){

}

Como ya se ha visto, se trata de una función sencilla pero crucial que no toma argumentos ni devuelve nada. Se trata simplemente de una función nula, lo que significa que no tiene que devolver nada. Esta función se utiliza en los Asesores Expertos y se ejecuta cuando hay un nuevo tick, es decir, un cambio en las cotizaciones del precio de la materia prima en cuestión.

Así que ahora que hemos visto que la función OnTick se genera en cada cambio en las cotizaciones de precios, necesitamos definir alguna lógica de control que utilizaremos más adelante para controlar la ejecución de fragmentos de código específicos, de forma que se ejecuten una vez por barra y no en cada tick, al menos para evitar ejecuciones de código innecesarias, ahorrando así la memoria del dispositivo. Eso será necesario cuando busque máximos y mínimos oscilantes. No necesitamos buscar cada tick, aunque siempre obtendremos los mismos resultados, siempre que sigamos en la misma vela. He aquí la lógica:

   static bool isNewBar = false;
   int currBars = iBars(_Symbol,_Period);
   static int prevBars = currBars;
   if (prevBars == currBars){isNewBar = false;}
   else if (prevBars != currBars){isNewBar = true; prevBars = currBars;}

En primer lugar, declaramos una variable booleana estática llamada «isNewBar» y la inicializamos con el valor «false». El propósito de esta variable es rastrear si se ha formado una nueva barra en el gráfico. Declaramos la variable local con la palabra clave «static» para que pueda conservar su valor durante toda la vida útil de la función. Esto significa que no será dinámica. Normalmente, nuestra variable siempre será igual a false no a menos que la cambiemos posteriormente a true, y cuando se cambie, conservará su valor y no se actualizará en el siguiente tick, al contrario que cuando es dinámica, donde siempre se actualizará al valor de inicialización.

A continuación, declaramos otra variable entera «currBars» que almacena el número calculado de barras actuales en el gráfico para el símbolo de negociación y el periodo especificados o, mejor dicho, el marco temporal, como lo habrá oído. Esto se consigue mediante el uso de la función iBars, que sólo toma dos argumentos, es decir, símbolo y punto.

De nuevo, declaramos otra variable estática entera «prevBars» para almacenar el número total de barras anteriores en el gráfico cuando se genera una nueva barra, y seguimos inicializándola con el valor de las barras actuales en el gráfico para la primera ejecución de la función. Lo utilizaremos para comparar el número actual de barras con el número anterior, para determinar la instancia de una nueva generación de barras en el gráfico.

Por último, utilizamos una sentencia condicional para comprobar si el número actual de compases es igual al número anterior de compases. Si son iguales, significa que no se ha formado ninguna barra nueva, por lo que la variable «isNewBar» sigue siendo 'false'. En caso contrario, si el recuento de la barra actual y el de la anterior no son iguales, indica que se ha formado una nueva barra. En este caso, establecemos la variable «isNewBar» en 'true', y actualizamos «prevBars» para que coincida con el recuento de barras actual. Así, con este fragmento de código, podemos hacer un seguimiento de si se ha formado una nueva barra, y utilizar el resultado más adelante para asegurarnos de que sólo ejecutamos una instancia una vez por barra.

Ahora, podemos proceder a buscar puntos de oscilación en nuestro gráfico. Necesitaremos una serie de exploraciones para los puntos. Para ello, seleccionamos una barra concreta y escaneamos todas las barras vecinas, a derecha e izquierda, por supuesto dentro del rango de barras predefinido, y determinamos si la barra actual es la más alta dentro del rango en caso de un swing alto, o la más baja en caso de un swing bajo. Así que primero, definamos las variables que necesitaremos para almacenar esta lógica.

   const int length = 20;
   const int limit = 20;

Aquí declaramos dos variables enteras «length» y «limit». La longitud (length) representa el rango de barras a tener en cuenta a la hora de identificar los máximos y mínimos de las oscilaciones, mientras que el límite (limit) representa el índice de la barra actual que se está analizando en ese momento concreto. Por ejemplo, supongamos que hemos seleccionado una barra en el índice 10 para escanearla e identificar si se trata de un máximo oscilatorio. Luego recorremos todas las barras vecinas hacia la derecha y hacia la izquierda y buscamos si hay otra barra que sea más alta que la barra actual, que está en el índice 10. Por lo tanto, la barra de la izquierda es la barra anterior a la barra actual y, por lo tanto, se encuentra en el índice (límite, que es igual a 10, + 1) 11. El mismo caso ocurre cuando se procede hacia la derecha.

De forma predeterminada, inicializamos las variables a 20. Además, deberías haber notado que los declaramos como "const" para hacerlos constantes. Esto se hace para garantizar que su valor permanezca fijo durante toda la ejecución del programa, lo que genera una consistencia que ayuda a mantener el mismo rango de análisis para los puntos de oscilación en diferentes barras. Mantener los valores constantes también ayuda a prevenir la modificación accidental de las variables durante la ejecución del programa.

Definamos entonces rápidamente las otras variables cruciales del programa. Necesitamos realizar un seguimiento de la barra actual que se está analizando y evaluar su relación con las barras vecinas dentro del rango predefinido. Logramos esto mediante la declaración de las siguientes variables.

   int right_index, left_index;
   bool isSwingHigh = true, isSwingLow = true;
   static double swing_H = -1.0, swing_L = -1.0;
   int curr_bar = limit;

Primero declaramos dos variables enteras "right_index" y "left_index" para realizar un seguimiento de los índices de las barras vecinas. El índice derecho representa el índice de la barra a la derecha de la barra actual, mientras que el índice izquierdo representa el índice de la barra a la izquierda de la barra actual, que es la barra seleccionada para el análisis. Nuevamente, declaramos dos variables booleanas "isSwingHigh" e "isSwingLow" que sirven como indicadores para determinar si la barra actual es un potencial swing alto o bajo respectivamente, y las inicializamos como verdaderas. Después del análisis, si alguna de las banderas permanece verdadera, indicará la presencia de un punto de inflexión. Además, declaramos las variables dobles estáticas "swing_H" y "swing_L" que almacenarán los niveles de precios de los máximos y mínimos del swing respectivamente. Los inicializamos con valores de -1 para indicar simplemente que aún no se ha detectado ningún máximo ni mínimo. Se hacen estáticos para garantizar que una vez que tengamos los puntos de giro, estos permanezcan inalterados y podamos almacenarlos para futuras referencias, para identificar más adelante si se rompen por un cambio de estructura. Los cambiaremos a -1 si tenemos una ruptura de estructura, o serán reemplazados por nuevos puntos de giro que se generen. Por último, tenemos la variable "curr_bar" que determina el punto de partida del análisis.

Hasta este punto, hemos declarado perfecta y suficientemente todas las variables que son cruciales para el programa y podemos comenzar nuestro ciclo de análisis. Para analizar y mapear los puntos de swing, solo necesitamos hacerlo una vez por barra. Por lo tanto, el análisis de los puntos de oscilación solo se realizará una vez por barra, y aquí es donde nuestra variable "isNewBar" resulta útil.

   if (isNewBar){ ... }

Luego instanciamos un bucle for para encontrar los máximos y mínimos de la oscilación.

      for (int j=1; j<=length; j++){
         right_index = curr_bar - j;
         left_index = curr_bar + j;
         if ( (high(curr_bar) <= high(right_index)) || (high(curr_bar) < high(left_index)) ){
            isSwingHigh = false;
         }
         if ( (low(curr_bar) >= low(right_index)) || (low(curr_bar) > low(left_index)) ){
            isSwingLow = false;
         }
      }

Declaramos una variable entera de bucle "j" para representar el número de barras a considerar al comparar la barra actual con sus vecinas. Luego calculamos el índice de la barra a la derecha de la barra actual restando "j" de la barra actual. Usando la misma lógica, obtenemos el índice de la barra vecina en el lado izquierdo agregando "j" a la barra actual. Si imprimimos los resultados por razones de visualidad, esto es lo que obtendremos:

Índice de barras

Las declaraciones de impresión se lograron mediante el uso de la siguiente función incorporada:

         Print("Current Bar Index = ",curr_bar," ::: Right index: ",right_index,", Left index: ",left_index);

Hasta este punto, está clarísimo que para el índice de barra seleccionado, en este caso 20, evaluamos todas las barras vecinas a la izquierda y a la derecha dentro de la longitud especificada. Es evidente que en cada iteración, negamos uno a la derecha y sumamos uno a la izquierda, lo que da como resultado que el índice derecho alcance el valor cero, que generalmente significa la barra actual, y el índice izquierdo duplica la longitud predefinida. Ahora que estamos seguros de que hemos realizado correctamente la evaluación de la barra, procedemos a determinar la presencia de los puntos de oscilación en cada iteración. 

Para determinar si hay un máximo, utilizamos una declaración condicional para verificar si el precio máximo de la barra actual es menor o igual al precio máximo de la barra en el índice derecho o menor que el precio máximo de la barra en el índice izquierdo. Si alguna de las condiciones es verdadera, significa que la barra actual no tiene un máximo más alto en comparación con sus vecinas, por lo que "isSwingHigh" se establece como falso. Para determinar si hay un swing low, prevalece la misma lógica, pero con condiciones inversas.

Al final del bucle, si "isSwingHigh" sigue siendo verdadero, sugiere que la barra actual tiene un máximo más alto que las barras circundantes dentro del rango de longitud, lo que marca un posible máximo de oscilación. La misma lógica todavía se aplica a la bandera de swing bajo. Si eso es cierto, llenamos las variables de punto de oscilación con los precios respectivos y dibujamos los puntos de oscilación.

      if (isSwingHigh){
         swing_H = high(curr_bar);
         Print("UP @ BAR INDEX ",curr_bar," of High: ",high(curr_bar));
         drawSwingPoint(TimeToString(time(curr_bar)),time(curr_bar),high(curr_bar),77,clrBlue,-1);
      }
      if (isSwingLow){
         swing_L = low(curr_bar);
         Print("DOWN @ BAR INDEX ",curr_bar," of Low: ",low(curr_bar));
         drawSwingPoint(TimeToString(time(curr_bar)),time(curr_bar),low(curr_bar),77,clrRed,1);
      }

Se utilizan funciones personalizadas para obtener los precios altos de los puntos altos del swing y los precios bajos de los puntos bajos del swing. Las funciones se declaran de la siguiente manera:

double high(int index){return (iHigh(_Symbol,_Period,index));}
double low(int index){return (iLow(_Symbol,_Period,index));}
double close(int index){return (iClose(_Symbol,_Period,index));}
datetime time(int index){return (iTime(_Symbol,_Period,index));}

La función alta toma un solo parámetro o argumento, que representa el índice de la barra dentro de la serie de datos de precios, del cual se debe recuperar el precio alto de la barra especificada en el índice dado. La misma lógica se aplica a las funciones baja, cerrada y de tiempo.

Para dibujar el punto de oscilación en el gráfico en la barra respectiva con fines de visualización, utilizamos la siguiente función personalizada:

void drawSwingPoint(string objName,datetime time,double price,int arrCode,
   color clr,int direction){
   
   if (ObjectFind(0,objName) < 0){
      ObjectCreate(0,objName,OBJ_ARROW,0,time,price);
      ObjectSetInteger(0,objName,OBJPROP_ARROWCODE,arrCode);
      ObjectSetInteger(0,objName,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objName,OBJPROP_FONTSIZE,10);
      if (direction > 0) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_TOP);
      if (direction < 0) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_BOTTOM);
      
      string txt = " BoS";
      string objNameDescr = objName + txt;
      ObjectCreate(0,objNameDescr,OBJ_TEXT,0,time,price);
      ObjectSetInteger(0,objNameDescr,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objNameDescr,OBJPROP_FONTSIZE,10);
      if (direction > 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT_UPPER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }
      if (direction < 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }
   }
   ChartRedraw(0);
}

La función personalizada "drawSwingPoint" toma seis parámetros para facilitar su reutilización. Las funciones de los parámetros son las siguientes:

  • objName: Una cadena (string) que representa el nombre del objeto gráfico que se va a crear.
  • time: Un valor datetime que indica la coordenada horaria en la que debe situarse el objeto.
  • price: Un valor doble que representa la coordenada de precio en la que debe colocarse el objeto.
  • arrCode: Un número entero que especifica el código de la flecha para el objeto flecha.
  • clr: Un valor de color (por ejemplo, clrBlue, clrRed) para los objetos gráficos.
  • direction: Un número entero que indica la dirección (arriba o abajo) para posicionar la etiqueta de texto.

La función primero verifica si un objeto con el objName especificado ya existe en el gráfico. En caso contrario, procede a crear los objetos. La creación del objeto se logra mediante el uso de la función incorporada "ObjectCreate", que requiere la especificación del objeto a dibujar, en este caso, el objeto de flecha identificado como "OBJ_ARROW", así como el tiempo y el precio, que forman las ordenadas del punto de creación del objeto. Después, establecemos las propiedades del objeto: código de flecha, color, tamaño de fuente y punto de anclaje. Para el código de la flecha, MQL5 dispone de algunos caracteres ya predefinidos de la fuente Wingdings que se pueden utilizar directamente. A continuación se muestra una tabla que especifica los caracteres:

Códigos de flechas

Hasta este punto, solo dibujamos la flecha especificada en el gráfico de la siguiente manera:

Punto de oscilación sin descripción

Podemos ver que logramos dibujar los puntos de giro con el código de flecha especificado, en este caso usamos el código de flecha 77, pero no hay una descripción de ellos. Por lo tanto, para agregar la descripción respectiva, procedemos a concatenar la flecha con un texto. Creamos otro objeto de texto especificado como "OBJ_TEXT" y también establecemos sus respectivas propiedades. La etiqueta de texto sirve como una anotación descriptiva asociada con el punto de oscilación, al proporcionar contexto o información adicional sobre el punto de oscilación, haciéndolo más informativo para los comerciantes y analistas. Elegimos el valor del texto como "BoS", lo que significa que es un punto de giro.

Luego se crea la variable "objNameDescr" concatenando el "objName" original con el texto descriptivo. Este nombre combinado garantiza que la flecha y su etiqueta de texto asociada estén vinculadas entre sí. Este fragmento de código específico se utiliza para lograrlo.

      string txt = " BoS";
      string objNameDescr = objName + txt;
      ObjectCreate(0,objNameDescr,OBJ_TEXT,0,time,price);
      ObjectSetInteger(0,objNameDescr,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objNameDescr,OBJPROP_FONTSIZE,10);
      if (direction > 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT_UPPER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }
      if (direction < 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }

Esto es lo que obtenemos como resultado de la concatenación del punto de oscilación con su descripción.

Punto de giro con descripción

El código completo responsable del análisis de las barras, la identificación de los máximos y mínimos del swing, la documentación de los datos y el mapeo respectivo de los objetos a los puntos de swing del gráfico es el siguiente:

   if (isNewBar){
      for (int j=1; j<=length; j++){
         right_index = curr_bar - j;
         left_index = curr_bar + j;

         if ( (high(curr_bar) <= high(right_index)) || (high(curr_bar) < high(left_index)) ){
            isSwingHigh = false;
         }
         if ( (low(curr_bar) >= low(right_index)) || (low(curr_bar) > low(left_index)) ){
            isSwingLow = false;
         }
      }
      
      if (isSwingHigh){
         swing_H = high(curr_bar);
         Print("UP @ BAR INDEX ",curr_bar," of High: ",high(curr_bar));
         drawSwingPoint(TimeToString(time(curr_bar)),time(curr_bar),high(curr_bar),77,clrBlue,-1);
      }
      if (isSwingLow){
         swing_L = low(curr_bar);
         Print("DOWN @ BAR INDEX ",curr_bar," of Low: ",low(curr_bar));
         drawSwingPoint(TimeToString(time(curr_bar)),time(curr_bar),low(curr_bar),77,clrRed,1);
      }
   }

A continuación, simplemente identificamos las instancias de ruptura de los puntos de oscilación como se describe en la parte teórica y, si existe una instancia, visualizamos la ruptura y las posiciones de mercado abiertas respectivamente. Esto debe hacerse en cada tick, por lo que lo hacemos sin la nueva restricción de barras. Primero declaramos los precios de compra y venta que utilizaremos para abrir las posiciones una vez que se cumplan las condiciones respectivas. Tenga en cuenta que esto también debe hacerse en cada tick para que podamos obtener las últimas cotizaciones de precios.

   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);

Aquí, declaramos las variables de tipo de datos dobles para almacenar los precios recientes y los normalizamos a los dígitos de la moneda del símbolo redondeando el número de punto flotante para mantener la precisión. 

Para determinar si ha habido un avance de precio y una ruptura del nivel máximo del swing, utilizamos una declaración condicional. Primero, verificamos si existe un punto máximo de oscilación, por la lógica de que es mayor que cero, simplemente porque no podemos superar algún punto máximo de oscilación que no tengamos ya. Luego, si ya tenemos un punto alto de oscilación, verificamos que el precio de oferta esté por encima del nivel alto de oscilación, para asegurar que la posición de compra se abra al precio de venta y los niveles de operación, es decir, stop loss y take profit que están asociados con el precio de oferta, estén correctamente mapeados en un punto por encima del nivel de ruptura. Por último, verificamos si el precio de cierre de la barra anterior está por encima del nivel máximo de oscilación, para asegurarnos de que tenemos una ruptura válida que cumple con los requisitos. Si se cumplen todas las condiciones, entonces tenemos una ruptura de estructura (BoS) válida e imprimimos la instancia en el diario.

   if (swing_H > 0 && Bid > swing_H && close(1) > swing_H){
      Print("BREAK UP NOW");
      ...
   }

Para visualizar la configuración de la ruptura, necesitaremos dibujar una flecha que se extienda desde el punto más alto del swing hasta la vela donde se produce la ruptura. Esto significa que necesitaremos dos coordenadas para los dos puntos de la flecha que se crearán, normalmente el comienzo de la flecha que se unirá al punto de giro, y el final de la flecha, que es la vela donde se produce la ruptura. Esto se representa mucho más fácilmente en una imagen como la que se muestra a continuación:

Coordenadas de puntos

Las dos coordenadas que necesitamos son el tiempo, mostrado como X y representado en el eje x, y el precio, mostrado como Y y representado en el eje y. Para obtener las segundas coordenadas, es decir la vela donde se produce la ruptura de la estructura, utilizamos el índice de barra actual, que normalmente es 0. Sin embargo, obtener el índice de la barra que contiene el punto más alto del swing es un poco complicado. Recuerde que solo almacenamos el precio de la vela de oscilación máxima. También podríamos almacenar el índice de barras al mismo tiempo que almacenamos el precio, pero sería completamente inútil ya que después se generarían nuevas barras. Esto no significa que no podamos encontrar el índice de la barra que contiene el punto de giro. Podemos recorrer los precios altos de las barras anteriores y encontrar una que coincida con nuestro punto más alto. A continuación se explica cómo se consigue esto.

      int swing_H_index = 0;
      for (int i=0; i<=length*2+1000; i++){
         double high_sel = high(i);
         if (high_sel == swing_H){
            swing_H_index = i;
            Print("BREAK HIGH @ BAR ",swing_H_index);
            break;
         }
      }

Primero declaramos una variable entera "swing_H_index" que contendrá nuestro índice alto de swing y lo inicializará a cero. Luego, use el bucle for para recorrer el doble de todas las barras predefinidas más un rango de barras adicional de 1000, solo una cantidad arbitraria de barras en las que se puede encontrar el punto de oscilación, este puede ser cualquier valor, y comparar el alto de la barra seleccionada con el punto alto de oscilación almacenado. Entonces, si encontramos una coincidencia, almacenamos el índice y salimos del bucle prematuramente ya que ya hemos encontrado nuestro índice de barra de oscilación alta. 

Utilizando el índice de barra alta de oscilación, ahora podemos recuperar las propiedades de la barra, en este caso, solo nos interesa el tiempo para marcar las coordenadas x del punto de inicio de la flecha. Utilizamos una función personalizada que no es muy diferente de la función anterior que usamos para mapear el código de flecha.

void drawBreakLevel(string objName,datetime time1,double price1,
   datetime time2,double price2,color clr,int direction){
   if (ObjectFind(0,objName) < 0){
      ObjectCreate(0,objName,OBJ_ARROWED_LINE,0,time1,price1,time2,price2);
      ObjectSetInteger(0,objName,OBJPROP_TIME,0,time1);
      ObjectSetDouble(0,objName,OBJPROP_PRICE,0,price1);
      ObjectSetInteger(0,objName,OBJPROP_TIME,1,time2);
      ObjectSetDouble(0,objName,OBJPROP_PRICE,1,price2);
      ObjectSetInteger(0,objName,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objName,OBJPROP_WIDTH,2);
      
      string txt = " Break   ";
      string objNameDescr = objName + txt;
      ObjectCreate(0,objNameDescr,OBJ_TEXT,0,time2,price2);
      ObjectSetInteger(0,objNameDescr,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objNameDescr,OBJPROP_FONTSIZE,10);
      if (direction > 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_RIGHT_UPPER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }
      if (direction < 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_RIGHT_LOWER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }
   }
   ChartRedraw(0);
}

Aquí están las diferencias en la función respecto al anterior.

  1. Declaramos el nombre de la función como "drawBreakLevel".
  2. El objeto que creamos es una línea con flecha identificada como "OBJ_ARROWED_LINE".
  3. Nuestra línea con flechas contiene dos coordenadas con tiempo 1 y precio 1 para la primera, y tiempo 2 y precio 2 para la segunda coordenada.
  4. El texto de concatenación es "Break", lo que indica que se produjo una ruptura de estructura (BoS).

Luego usamos la función para dibujar la línea de flecha del nivel de ruptura en el gráfico. Para el tiempo 2 de la segunda coordenada, simplemente sumamos 1, lo que nos lleva a la barra anterior a la barra actual para mayor precisión. Luego restablecemos el valor de la variable swing high a -1, para indicar que ya hemos roto la estructura y la configuración ya no existe. Esto ayuda a evitar buscar la ruptura del máximo oscilante en los ticks anteriores, ya que ya hemos roto el punto máximo oscilante. De esta manera, simplemente esperamos a que se forme otro punto alto de oscilación, y la variable se llena nuevamente y el bucle continúa.

      drawBreakLevel(TimeToString(time(0)),time(swing_H_index),high(swing_H_index),
      time(0+1),high(swing_H_index),clrBlue,-1);
      
      swing_H = -1.0;

Finalmente, abrimos una posición de compra una vez que tenemos la ruptura del punto alto del swing. 

      //--- Open Buy
      obj_Trade.Buy(0.01,_Symbol,Ask,Bid-500*7*_Point,Bid+500*_Point,"BoS Break Up BUY");
      
      return;

Usamos nuestro objeto "obj_Trade" y el operador punto para obtener acceso a todos los métodos contenidos en la clase. En este caso, solo necesitamos comprar y por lo tanto utilizar el método "Comprar", proporcionando el volumen, los niveles comerciales y el comentario comercial. Finalmente volvemos ya que todo está listo y no tenemos más código para ejecutar. Sin embargo, si tiene más código, simplemente evite usar el operador de retorno, ya que finaliza la ejecución de la función actual y devuelve el control al programa que la llama. El código completo que garantiza que encontremos las rupturas de la estructura, dibujemos las líneas con flechas y abramos posiciones de compra es el siguiente:

   if (swing_H > 0 && Bid > swing_H && close(1) > swing_H){
      Print("BREAK UP NOW");
      int swing_H_index = 0;
      for (int i=0; i<=length*2+1000; i++){
         double high_sel = high(i);
         if (high_sel == swing_H){
            swing_H_index = i;
            Print("BREAK HIGH @ BAR ",swing_H_index);
            break;
         }
      }
      drawBreakLevel(TimeToString(time(0)),time(swing_H_index),high(swing_H_index),
      time(0+1),high(swing_H_index),clrBlue,-1);
      
      swing_H = -1.0;
      
      //--- Open Buy
      obj_Trade.Buy(0.01,_Symbol,Ask,Bid-500*7*_Point,Bid+500*_Point,"BoS Break Up BUY");
      
      return;
   }

Para la ruptura de mínimos oscilantes, el trazado simultáneo de las líneas de ruptura con flechas y la apertura de posiciones de venta, prevalece la misma lógica sólo con condiciones inversas. Su código completo es el siguiente:

   else if (swing_L > 0 && Ask < swing_L && close(1) < swing_L){
      Print("BREAK DOWN NOW");
      int swing_L_index = 0;
      for (int i=0; i<=length*2+1000; i++){
         double low_sel = low(i);
         if (low_sel == swing_L){
            swing_L_index = i;
            Print("BREAK LOW @ BAR ",swing_L_index);
            break;
         }
      }
      drawBreakLevel(TimeToString(time(0)),time(swing_L_index),low(swing_L_index),
      time(0+1),low(swing_L_index),clrRed,1);

      swing_L = -1.0;
      
      //--- Open Sell
      obj_Trade.Sell(0.01,_Symbol,Bid,Ask+500*7*_Point,Ask-500*_Point,"BoS Break Down SELL");

      return;
   }

Aquí está la representación del hito.

Hito

El siguiente es el código completo que se necesita para crear una estrategia de trading de divisas de ruptura de estructura (BoS) en MQL5 que identifica las rupturas y abre posiciones respectivamente.

//+------------------------------------------------------------------+
//|                                                          BOS.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade/Trade.mqh>
CTrade obj_Trade;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){return(INIT_SUCCEEDED);}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
   
   static bool isNewBar = false;
   int currBars = iBars(_Symbol,_Period);
   static int prevBars = currBars;
   if (prevBars == currBars){isNewBar = false;}
   else if (prevBars != currBars){isNewBar = true; prevBars = currBars;}
   
   const int length = 5;
   const int limit = 5;

   int right_index, left_index;
   bool isSwingHigh = true, isSwingLow = true;
   static double swing_H = -1.0, swing_L = -1.0;
   int curr_bar = limit;
   
   if (isNewBar){
      for (int j=1; j<=length; j++){
         right_index = curr_bar - j;
         left_index = curr_bar + j;
         //Print("Current Bar Index = ",curr_bar," ::: Right index: ",right_index,", Left index: ",left_index);
         //Print("curr_bar(",curr_bar,") right_index = ",right_index,", left_index = ",left_index);
         // If high of the current bar curr_bar is <= high of the bar at right_index (to the left),
         //or if it’s < high of the bar at left_index (to the right), then isSwingHigh is set to false
         //This means that the current bar curr_bar does not have a higher high compared
         //to its neighbors, and therefore, it’s not a swing high
         if ( (high(curr_bar) <= high(right_index)) || (high(curr_bar) < high(left_index)) ){
            isSwingHigh = false;
         }
         if ( (low(curr_bar) >= low(right_index)) || (low(curr_bar) > low(left_index)) ){
            isSwingLow = false;
         }
      }
      //By the end of the loop, if isSwingHigh is still true, it suggests that 
      //current bar curr_bar has a higher high than the surrounding bars within
      //length range, marking a potential swing high.
      
      if (isSwingHigh){
         swing_H = high(curr_bar);
         Print("UP @ BAR INDEX ",curr_bar," of High: ",high(curr_bar));
         drawSwingPoint(TimeToString(time(curr_bar)),time(curr_bar),high(curr_bar),77,clrBlue,-1);
      }
      if (isSwingLow){
         swing_L = low(curr_bar);
         Print("DOWN @ BAR INDEX ",curr_bar," of Low: ",low(curr_bar));
         drawSwingPoint(TimeToString(time(curr_bar)),time(curr_bar),low(curr_bar),77,clrRed,1);
      }
   }
   
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);

   if (swing_H > 0 && Bid > swing_H && close(1) > swing_H){
      Print("BREAK UP NOW");
      int swing_H_index = 0;
      for (int i=0; i<=length*2+1000; i++){
         double high_sel = high(i);
         if (high_sel == swing_H){
            swing_H_index = i;
            Print("BREAK HIGH @ BAR ",swing_H_index);
            break;
         }
      }
      drawBreakLevel(TimeToString(time(0)),time(swing_H_index),high(swing_H_index),
      time(0+1),high(swing_H_index),clrBlue,-1);
      
      swing_H = -1.0;
      
      //--- Open Buy
      obj_Trade.Buy(0.01,_Symbol,Ask,Bid-500*7*_Point,Bid+500*_Point,"BoS Break Up BUY");
      
      return;
   }
   else if (swing_L > 0 && Ask < swing_L && close(1) < swing_L){
      Print("BREAK DOWN NOW");
      int swing_L_index = 0;
      for (int i=0; i<=length*2+1000; i++){
         double low_sel = low(i);
         if (low_sel == swing_L){
            swing_L_index = i;
            Print("BREAK LOW @ BAR ",swing_L_index);
            break;
         }
      }
      drawBreakLevel(TimeToString(time(0)),time(swing_L_index),low(swing_L_index),
      time(0+1),low(swing_L_index),clrRed,1);

      swing_L = -1.0;
      
      //--- Open Sell
      obj_Trade.Sell(0.01,_Symbol,Bid,Ask+500*7*_Point,Ask-500*_Point,"BoS Break Down SELL");

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

double high(int index){return (iHigh(_Symbol,_Period,index));}
double low(int index){return (iLow(_Symbol,_Period,index));}
double close(int index){return (iClose(_Symbol,_Period,index));}
datetime time(int index){return (iTime(_Symbol,_Period,index));}

void drawSwingPoint(string objName,datetime time,double price,int arrCode,
   color clr,int direction){
   
   if (ObjectFind(0,objName) < 0){
      ObjectCreate(0,objName,OBJ_ARROW,0,time,price);
      ObjectSetInteger(0,objName,OBJPROP_ARROWCODE,arrCode);
      ObjectSetInteger(0,objName,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objName,OBJPROP_FONTSIZE,10);
      if (direction > 0) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_TOP);
      if (direction < 0) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_BOTTOM);
      
      string txt = " BoS";
      string objNameDescr = objName + txt;
      ObjectCreate(0,objNameDescr,OBJ_TEXT,0,time,price);
      ObjectSetInteger(0,objNameDescr,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objNameDescr,OBJPROP_FONTSIZE,10);
      if (direction > 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT_UPPER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }
      if (direction < 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }
   }
   ChartRedraw(0);
}

void drawBreakLevel(string objName,datetime time1,double price1,
   datetime time2,double price2,color clr,int direction){
   if (ObjectFind(0,objName) < 0){
      ObjectCreate(0,objName,OBJ_ARROWED_LINE,0,time1,price1,time2,price2);
      ObjectSetInteger(0,objName,OBJPROP_TIME,0,time1);
      ObjectSetDouble(0,objName,OBJPROP_PRICE,0,price1);
      ObjectSetInteger(0,objName,OBJPROP_TIME,1,time2);
      ObjectSetDouble(0,objName,OBJPROP_PRICE,1,price2);
      ObjectSetInteger(0,objName,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objName,OBJPROP_WIDTH,2);
      
      string txt = " Break   ";
      string objNameDescr = objName + txt;
      ObjectCreate(0,objNameDescr,OBJ_TEXT,0,time2,price2);
      ObjectSetInteger(0,objNameDescr,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objNameDescr,OBJPROP_FONTSIZE,10);
      if (direction > 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_RIGHT_UPPER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }
      if (direction < 0) {
         ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_RIGHT_LOWER);
         ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
      }
   }
   ChartRedraw(0);
}

¡Bien por nosotros! Ahora hemos creado un sistema de comercio de concepto de dinero inteligente basado en la estrategia de comercio de divisas de ruptura de estructura (BoS) no solo para generar señales de comercio sino también para abrir posiciones de mercado basadas en las señales generadas.


Resultados del probador de estrategias

Al probar en el probador de estrategias, aquí están los resultados.

  • Gráfico de balance/equidad:

Gráfico

  • Resultados del backtest:

Resultados


Conclusión

En conclusión, podemos decir con confianza que la automatización de la estrategia de Ruptura de Estructura (BoS) no es tan compleja como se percibe una vez que se le da el pensamiento necesario. Técnicamente, puede ver que su creación sólo requirió una comprensión clara de la estrategia y de los requisitos reales, o más bien de los objetivos que deben cumplirse para crear una configuración válida de la estrategia. 

En general, el artículo hace hincapié en la parte teórica que debe tenerse en cuenta y entenderse claramente para crear una estrategia de negociación de divisas BoS. Esto implica su definición, descripción y tipos, además de su plano. Además, el aspecto de codificación de la estrategia destaca los pasos que se siguen para analizar las velas, identificar los puntos de oscilación, seguir sus rupturas, visualizar sus resultados y abrir posiciones de negociación en función de las señales generadas. A largo plazo, esto permite automatizar la estrategia de la balanza de pagos, facilitando una ejecución más rápida y la escalabilidad de la estrategia.

Descargo de responsabilidad: La información ilustrada en este artículo sólo tiene fines educativos. Solo tiene como objetivo mostrar información sobre cómo crear un Asesor Experto (EA) de Ruptura de Estructura (BoS) basado en el enfoque del Concepto de Dinero Inteligente (SMC, Smart Money Concept) y, por lo tanto, debe usarse como base para crear un mejor asesor experto con mayor optimización y extracción de datos en cuenta. La información presentada no garantiza ningún resultado comercial.

Esperamos que haya encontrado el artículo útil, divertido y fácil de entender, de forma que pueda hacer uso de los conocimientos presentados en su desarrollo de futuros asesores expertos. Técnicamente, esto facilita su forma de analizar el mercado basándose en el enfoque del Concepto de Dinero Inteligente (SMC) y, en particular, en la estrategia de Ruptura de Estructura (BoS).


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

Creación de un modelo de restricción de tendencia de velas (Parte 4): Personalización del estilo de visualización para cada onda de tendencias Creación de un modelo de restricción de tendencia de velas (Parte 4): Personalización del estilo de visualización para cada onda de tendencias
En este artículo, exploraremos las capacidades del poderoso lenguaje MQL5 para dibujar varios estilos de indicadores en MetaTrader 5. También veremos los scripts y cómo pueden utilizarse en nuestro modelo.
Integración de modelos ocultos de Márkov en MetaTrader 5 Integración de modelos ocultos de Márkov en MetaTrader 5
En este artículo demostramos cómo los modelos ocultos de Márkov entrenados con Python pueden integrarse en las aplicaciones de MetaTrader 5. Los modelos ocultos de Márkov son una potente herramienta estadística utilizada para modelar datos de series temporales, en los que el sistema modelado se caracteriza por estados no observables (ocultos). Una premisa fundamental de los modelos ocultos de Márkov es que la probabilidad de estar en un estado determinado en un momento concreto depende del estado del proceso en el intervalo de tiempo anterior.
Kit de herramientas de negociación MQL5 (Parte 1): Desarrollo de una biblioteca EX5 de gestión de posiciones Kit de herramientas de negociación MQL5 (Parte 1): Desarrollo de una biblioteca EX5 de gestión de posiciones
Aprenda a crear un conjunto de herramientas de desarrollador para gestionar diversas operaciones de posición con MQL5. En este artículo, demostraré cómo crear una librería de funciones (ex5) que realizarán operaciones de gestión de posiciones simples a avanzadas, incluyendo el manejo automático y la notificación de los diferentes errores que surgen al tratar con tareas de gestión de posiciones con MQL5.
Características del Wizard MQL5 que debe conocer (Parte 22): Redes generativas adversativas (RGAs) condicionales Características del Wizard MQL5 que debe conocer (Parte 22): Redes generativas adversativas (RGAs) condicionales
Las redes generativas antagónicas son un emparejamiento de redes neuronales que se entrenan entre sí para obtener resultados más precisos. Adoptamos el tipo condicional de estas redes mientras buscamos una posible aplicación en la previsión de series de tiempo financieras dentro de una clase de señales expertas.