English Русский Deutsch 日本語
preview
Aprenda a operar la brecha de valor justo (Fair Value Gap, FVG) y los desequilibrios paso a paso: Enfoque basado en el concepto de dinero inteligente (Smart Money Concept, SMC)

Aprenda a operar la brecha de valor justo (Fair Value Gap, FVG) y los desequilibrios paso a paso: Enfoque basado en el concepto de dinero inteligente (Smart Money Concept, SMC)

MetaTrader 5Trading | 18 septiembre 2024, 09:36
48 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Introducción

En este artículo, discutiremos los pasos básicos hacia la elaboración y el desarrollo de un asesor experto (Expert Advisor, EA), basado en la estrategia de la brecha del valor justo (FVG)/desequilibrio y un enfoque del concepto de dinero inteligente. En esencia, el camino hacia la creación de un asesor experto basado en la estrategia Fair Value Gap representa una convergencia de arte y ciencia, que normalmente requiere que el operador sea capaz no sólo de analizar las velas, sino también de dibujar para visualizar los niveles conceptuales. Únase a nosotros para desentrañar los misterios del dinero inteligente y embarcarse en la búsqueda de su poder transformador en el ámbito de la negociación algorítmica. Elaboraremos el asesor experto con Fair Value Gap a través de los siguientes temas:

  1. Definición de desequilibrio
  2. Descripción de la estrategia de negociación
  3. Plano de la estrategia de negociación
  4. Sistema de negociación en MQL5
  5. Resultados de las pruebas de estrategia
  6. 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 ello, será de vital importancia contar con las versiones mencionadas. Comencemos.


Definición de la brecha de valor justo (Fair Value Gap, FVG)/desequilibrio

Fair Value Gap (FVG) delimita la diferencia entre los desequilibrios causados tanto por las presiones compradoras como por las vendedoras. Normalmente, se producen cuando hay una gran volatilidad que expulsa por completo del mercado a los osos o a los toros y, por tanto, se desarrollan presiones desiguales en el mercado. Esto se caracteriza típicamente por movimientos unidireccionales masivos en el mercado. Normalmente, observará largas velas en el gráfico. Armados con este conocimiento, podemos emplear varios enfoques para aprovechar la brecha de valor justo (Fair Value Gap, FVG) y, potencialmente, crear una estrategia de negociación.

Descripción de la estrategia de negociación de la brecha de valor justo (Fair Value Gap, FVG)

La estrategia de negociación de la brecha del valor justo integra el concepto de evaluación del valor justo con los desequilibrios de las velas para identificar posibles oportunidades de negociación.

He aquí una descripción detallada de la estrategia de negociación de la brecha del valor justo que incorpora los desequilibrios de las velas:

  • Los operadores realizan un análisis fundamental exhaustivo para determinar las brechas de valor justo. Esto se consigue analizando la acción de los precios del mercado mediante patrones de velas para identificar desequilibrios entre las presiones de compra y de venta. Las pautas de las velas, como la envolvente alcista, la envolvente bajista y el doji, ofrecen información sobre el sentimiento del mercado y los posibles cambios de impulso. Los operadores buscan casos en los que el precio de mercado se desvíe significativamente del valor razonable estimado, al tiempo que observan desequilibrios notables en las velas. Una amplia brecha entre el precio de cierre de la sesión anterior y el precio de apertura de la sesión actual, junto con fuertes patrones de velas alcistas o bajistas, puede indicar una posible brecha de valor justo. El aspecto más difícil del enfoque de la brecha del valor justo, al igual que otros patrones gráficos, es detectar esta configuración especial en un gráfico de precios. Las FVGs requieren la aparición de un diseño de tres velas con pautas específicas. Cuando esto ocurre, la brecha del valor justo es el área o distancia entre las mechas de la primera y la tercera vela.

A continuación se explica cómo detectar una FVG en el gráfico:

  • Encontrar la vela grande: Lo primero que hay que hacer cuando se trata de averiguar un hueco de valor justo es buscar en su gráfico de precios una vela grande. Debe haber una proporción notable entre el cuerpo y la mecha de esta vela; idealmente, 70%.
  • Examinando las velas adyacentes: Después de haber localizado la vela grande, examina las que están justo delante y detrás de ella. La vela importante no debe superponerse completamente con estas velas cercanas. Más bien, podría haber ligeros solapamientos en los lados superior e inferior de la vela significativa. La brecha del valor justo se produce entonces por el espacio entre las mechas de las velas adyacentes.
  • Determinación de la diferencia de valor justo: La diferencia de valor justo debe definirse y representarse en su tabla de precios como último paso. El rango de precios entre el máximo y el mínimo de la vela precedente en una tendencia bajista se conoce como hueco de valor justo. Aquí es donde se muestra el desequilibrio del mercado y podría presentar una oportunidad de negociación. Lo mismo ocurre con una tendencia alcista, pero con condiciones opuestas.



  • Señales de entrada y salida: Al identificar una brecha de valor justo acompañada de desequilibrios significativos en las velas, los operadores ejecutan las operaciones en consecuencia. Por ejemplo, si el precio de mercado es inferior al valor justo estimado y aparece un patrón envolvente alcista, los operadores pueden considerar entrar en una posición larga, anticipándose a una corrección del precio. Por el contrario, si el precio de mercado es superior al valor justo y aparece un patrón envolvente bajista, los operadores pueden iniciar una posición corta.
  • Gestión del riesgo: Los traders implementan técnicas de gestión de riesgos, como establecer órdenes de stop-loss y definir el tamaño de las posiciones, para mitigar las posibles pérdidas en caso de que la operación no se desarrolle como se anticipó.
  • Monitoreo y ajuste: Los traders monitorean continuamente las operaciones, ajustando sus posiciones según las condiciones cambiantes del mercado y la reevaluación de las estimaciones de valor justo.

Como ya habrá notado, existen dos variedades diferentes de brechas de valor justo, cada una con ramificaciones únicas para los operadores. A continuación encontrará una descripción detallada:

  • Brecha de valor justo bajista, o brecha de valor justo infravalorado

Este tipo de FVG indica que el precio de un par de divisas, o el precio de cualquier otro activo financiero, es actualmente inferior a su valor justo. En pocas palabras, los operadores pueden esperar un retroceso del mercado para hacer frente a esta ineficacia. Si ve una gran vela bajista en su gráfico, probablemente indica que hay un FVG infravalorado.

  • Brecha alcista del valor justo, o brecha sobrevalorada del valor justo

Por otro lado, una FVG sobrevalorada muestra que un activo financiero o un par de divisas cotiza actualmente por encima de su valor justo. El mercado está sobrecalentado y la corrección es inminente. Antes de subir, los operadores deberían anticipar un retroceso de los precios a medida que el mercado se corrige a sí mismo.


Modelo de estrategia de negociación de la brecha del valor justo (FVG)

Tras definir y describir la estrategia de la brecha del valor justo, definamos ahora paso a paso las condiciones designadas que deben tenerse en cuenta para operar con la estrategia FVG. Recordemos que en el mercado pueden formarse dos tipos de FVG.

  • FVG alcista: De acuerdo con la estrategia, necesitamos encontrar una vela alcista que tenga un movimiento importante del precio y luego evaluar las velas vecinas (a la izquierda y a la derecha). Si esto es cierto, entonces procedemos a obtener la diferencia entre la tercera vela y la primera de forma de serie temporal, y si la diferencia no está dentro de los puntos restringidos predefinidos, tenemos una FVG alcista que operaremos en consecuencia. El FVG se convierte en nuestro punto de interés y lo documentamos en el algoritmo. Al mismo tiempo trazamos la FVG, de color verde/lima en el gráfico, con una longitud predefinida a efectos de visualización, lo que indica que hemos encontrado una configuración de FVG alcista y que estamos listos para negociar la configuración. Así que ahora, si el precio vuelve y toca la zona inferior de la FVG, enviamos una orden de compra de mercado instantánea. El take profit se fijará en la parte superior de la FVG, mientras que el stop loss se fijará por debajo del precio de apertura de la orden, con una relación riesgo/recompensa de 1:3. Sin embargo, si el precio no vuelve a la configuración FVG después de la duración predefinida, nos despreocupamos.


El mismo proceso continúa para todas las demás configuraciones alcistas de la FVG.

  • FVG bajista: De nuevo, aquí, tenemos que encontrar una vela bajista que tenga un movimiento importante del precio y luego evaluar las velas vecinas (a la izquierda y a la derecha). Si esto es cierto, entonces procedemos a obtener la diferencia entre la tercera vela y la primera de manera serie temporal, y si la diferencia no está dentro de los puntos predefinidos restringidos, tenemos una FVG bajista que operaremos en consecuencia. El FVG se convierte en nuestro punto de interés y lo documentamos en el algoritmo. Al mismo tiempo, dibujamos la FVG, de color rojo/tomate, en el gráfico con una longitud predefinida con fines de visualización, lo que indica que hemos encontrado una configuración de FVG bajista y que estamos listos para operar la configuración. Así que ahora, si el precio vuelve y toca la zona superior de la FVG, enviamos una orden de venta a mercado instantánea. El take profit se fijará en la parte inferior de la FVG, mientras que el stop loss se fijará por encima del precio de apertura de la orden, con una relación riesgo/recompensa de 1:10. Sin embargo, si el precio no vuelve a la configuración FVG después de la duración predefinida, nos despreocupamos.


El mismo proceso continúa para todas las demás configuraciones bajistas de la FVG.


Sistema de negociación de la brecha del valor justo (FVG) en MQL5

Después de aprender toda la teoría sobre la estrategia de trading FVG, vamos a automatizar la teoría y crear un asesor experto (EA) en 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 lenguaje de MetaQuotes, que permite la escritura de robots comerciales, indicadores técnicos, scripts y librerías de funciones.


Ahora haga clic en Nuevo, marque Asesor Experto (plantilla) y haga clic en Siguiente.


MetaEditor: Abrir un nuevo archivo.

A continuación, proporcione el nombre de archivo de asesor experto que desee, haga clic en Siguiente, en Siguiente y en Finalizar. Después de hacer todo esto, ya estamos listos para codificar y programar nuestra estrategia FVG.

En primer lugar, incluimos una instancia de comercio utilizando #include al principio del código fuente. Esto nos da acceso a la clase CTrade, que utilizaremos para crear un objeto comercial.

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

De nuevo, utilizamos #define para definir una variable que asignaremos como prefijo a todos los rectángulos FVG creados y a sus colores.

#define FVG_Prefix "FVG REC "
#define CLR_UP clrLime
#define CLR_DOWN clrRed

A continuación se ilustra la importancia de los parámetros mencionados.


Necesitaremos predefinir algunas otras variables para facilitar aún más la codificación del EA. Estos son los puntos mínimos de un rango de barras de desequilibrio para que se considere viable, y la longitud del rectángulo dibujado es, en este caso, las barras de extensión desde la barra de desequilibrio central. Las definiremos como variables globales.

int minPts = 100;
int FVG_Rec_Ext_Bars = 10;

A continuación se muestra la ilustración. Definimos minPts para los puntos mínimos y FVG_Rec_Bars para el rango en barras en el que se representará la longitud del rectángulo.


Por último, definimos cuatro matrices, de variables de tipo string, integer, datetime y boolean, que contendrán y almacenarán nuestros datos utilizados en la creación del EA. De nuevo, se trata de globales.

string totalFVGs[];
int barINDICES[];
datetime barTIMEs[];
bool signalFVGs[];

En la sección OnInit, buscamos las configuraciones FVG sólo para las barras visibles del gráfico. Esto dará a nuestro EA un toque más parecido al de los indicadores. Así, si el usuario inicia el gráfico, aún puede ver las configuraciones FVG anteriores y esperar a que se creen más en las velas anteriores. 

En primer lugar, obtenemos todas las barras visibles del gráfico e informamos de su recuento.

   int visibleBars = (int)ChartGetInteger(0,CHART_VISIBLE_BARS);
   Print("Total visible bars on chart = ",visibleBars);

Si no hay objetos rectángulo en el gráfico, liberamos y redimensionamos las matrices de almacenamiento a cero, para prepararnos para nuevos datos. Se trata de un paso muy importante, ya que el usuario podría haber borrado todos los objetos del gráfico antes de inicializar de nuevo el EA.

   if (ObjectsTotal(0,0,OBJ_RECTANGLE)==0){
      Print("No FVGs Found, Resizing storage arrays to 0 now!!!");
      ArrayResize(totalFVGs,0);
      ArrayResize(barINDICES,0);
      ArrayResize(signalFVGs,0);
   }

Para evitar la superposición de objetos, volvemos a deshacernos de todas las configuraciones FVG anteriores y creamos otras nuevas basadas en las propiedades del entorno gráfico aplicado en ese momento. Esto se consigue utilizando el prefijo de nuestras configuraciones FVG, lo que nos asegura que sólo eliminamos los objetos rectángulo FVG creados por nuestro EA, para que pueda ser compatible con otros EAs.

   ObjectsDeleteAll(0,FVG_Prefix);

A continuación, realizaremos un bucle a través de todas las barras visibles del gráfico, obtendremos las propiedades de la barra y, a continuación, comprobaremos si se cumple que se trata de una configuración FVG válida.

He aquí una descripción paso a paso:

Para buscar una configuración FVG alcista, obtenemos el mínimo de la primera barra, en el índice i, técnicamente esta es nuestra barra número 0 si supusiéramos que ya tenemos una configuración de sólo 3 barras, obtenemos el máximo de la tercera barra, en el índice i+2, y obtenemos su diferencia de hueco en forma de puntos.

      double low0 = iLow(_Symbol,_Period,i);
      double high2 = iHigh(_Symbol,_Period,i+2);
      double gap_L0_H2 = NormalizeDouble((low0 - high2)/_Point,_Digits);

La misma lógica se aplica a la búsqueda de configuraciones bajistas. Para buscar una configuración FVG bajista, obtenemos el máximo de la primera barra, en el índice i, técnicamente esta es nuestra barra número 0 si supusiéramos que ya tenemos una configuración de sólo 3 barras, obtenemos el mínimo de la tercera barra, en el índice i+2, y obtenemos su diferencia de hueco en forma de puntos.

      double high0 = iHigh(_Symbol,_Period,i);
      double low2 = iLow(_Symbol,_Period,i+2);
      double gap_H0_L2 = NormalizeDouble((low2 - high0)/_Point,_Digits);

Tras obtener la diferencia de puntos, ahora podemos utilizar los valores devueltos para comprobar si existe alguna de las configuraciones de la FVG. Para ello, utilizamos dos variables booleanas: 

  1. FVG_UP - Comprueba que el mínimo del índice de barra i es mayor que el máximo del índice de barra i+2 y, al mismo tiempo, que los puntos de separación calculados son mayores que los puntos de rectángulo mínimos permitidos.<<

  2. FVG_DOWN - Comprueba que el punto alto del índice de la barra i es menor que el punto bajo del índice de la barra i+2 y, al mismo tiempo, que los puntos de separación calculados son mayores que los puntos de rectángulo mínimos permitidos.

Aparentemente, la comprobación de los puntos mínimos garantiza que sólo tengamos configuraciones FVG válidas y con sentido, causadas por posibles movimientos espontáneos del precio, y no destroza nuestro gráfico.

      bool FVG_UP = low0 > high2 && gap_L0_H2 > minPts;
      bool FVG_DOWN = low2 > high0 && gap_H0_L2 > minPts;

Una vez que tenemos una configuración FVG confirmada, cualquiera, procedemos a crear la configuración FVG respectiva, junto con su documentación de datos en las matrices de almacenamiento.

Definimos la variable time1 de tipo de datos datetime, donde almacenamos el tiempo para la barra central, que es la del medio o segunda barra, en el índice i+1, donde comenzará el rectángulo.

De nuevo, definimos la variable price1 de tipo de datos double, donde utilizamos el operador ternario para devolver el máximo de la tercera barra en el caso de una configuración FVG alcista, o el máximo de la primera barra en el caso de una configuración FVG bajista.

Técnicamente, esto sirve como las coordenadas del primer punto de nuestro objeto rectángulo a dibujar.

         datetime time1 = iTime(_Symbol,_Period,i+1);
         double price1 = FVG_UP ? high2 : high0;

Tras definir las primeras coordenadas, definimos las segundas. 

Definimos la variable time2 de tipo de datos datetime, donde almacenamos el tiempo para la barra final, que es la barra en la que termina el rectángulo dibujado. Esto se consigue simplemente añadiendo al time1, el tiempo inicial, el número de barras para extender el rectángulo.

Una vez más, definimos la variable price2 de tipo de datos double, donde utilizamos el operador ternario para devolver el mínimo de la primera barra en el caso de una configuración FVG alcista, o el mínimo de la tercera barra en el caso de una configuración FVG bajista.

         datetime time2 = time1 + PeriodSeconds(_Period)*FVG_Rec_Ext_Bars;
         double price2 = FVG_UP ? low0 : low2;

A continuación se muestra una visualización de las coordenadas necesarias para garantizar una creación fluida de los rectángulos.


Tras obtener las coordenadas de la configuración FVG, tenemos que dar un nombre a ese objeto rectángulo FVG.

Utilizamos el prefijo predefinido como prefijo de nuestra FVG y le añadimos la hora de creación. Esto garantiza, en primer lugar, que una vez que creamos un objeto FVG a esa hora de la barra, no podemos volver a crearlo puesto que a esa hora de la vela en concreto ya existe un objeto de título similar, y en segundo lugar, que existe unicidad puesto que con toda seguridad no puede haber ninguna otra hora de la barra similar.

         string fvgNAME = FVG_Prefix+"("+TimeToString(time1)+")";

También debemos asignar diferentes colores a nuestras configuraciones de FVG para poder distinguir entre configuraciones alcistas y bajistas. A las configuraciones alcistas se les asigna el color alcista predefinido, es decir, CLR_UP, y a las configuraciones bajistas el color bajista predefinido, es decir, CLR_DOWN.

         color fvgClr = FVG_UP ? CLR_UP : CLR_DOWN;

Hasta aquí, tenemos todo lo que necesitamos para dibujar la respectiva configuración de la FVG en el gráfico. 

Para hacerlo fácilmente, procedemos y creamos una función void en el ámbito global. Definimos la función como CreateRec, y le pasamos el nombre del objeto, las coordenadas al punto uno y al punto dos respectivamente, y el color, variables que se necesitan en la creación de las configuraciones de la FVG.

void CreateRec(string objName,datetime time1,double price1,
               datetime time2, double price2,color clr){
   if (ObjectFind(0,objName) < 0){
      ObjectCreate(0,objName,OBJ_RECTANGLE,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_FILL,true);
      ObjectSetInteger(0,objName,OBJPROP_BACK,false);
      
      ChartRedraw(0);
   }
}

Utilice después la función creada para crear el rectángulo FVG correspondiente en el gráfico, pasando los parámetros preelaborados.

         CreateRec(fvgNAME,time1,price1,time2,price2,fvgClr);

Después de crear la configuración FVG, necesitamos almacenar los datos en las matrices de almacenamiento. Pero como hemos creado matrices dinámicas, lo primero que hacemos es redimensionarlas para incorporar un valor de datos adicional, utilizando la función ArrayResize.

         ArrayResize(totalFVGs,ArraySize(totalFVGs)+1);
         ArrayResize(barINDICES,ArraySize(barINDICES)+1);

Tras redimensionar las matrices de almacenamiento de datos, seguimos adelante y añadimos los nuevos datos a la matriz respectivamente.

         totalFVGs[ArraySize(totalFVGs)-1] = fvgNAME;
         barINDICES[ArraySize(barINDICES)-1] = i+1;

Hasta este punto, hemos conseguido crear configuraciones FVG para todas las barras visibles del gráfico. A continuación se muestra el resultado del hito.


El usuario podría estar necesitando que las configuraciones que podrían haber sido confirmadas sean cortadas una vez confirmadas, como una forma de decir que ya hubo una configuración FVG exitosa. Así que tenemos que truncar la longitud extra de las configuraciones una vez que el precio las confirme. Lo conseguimos haciendo un bucle a través de todas las configuraciones FVG totales creadas y confirmadas, obteniendo los detalles respectivos, comprobando los datos con la lógica de control y, por último, actualizando las configuraciones respectivamente.

A continuación le explicamos paso a paso cómo conseguirlo:

Realice un bucle a través de todas las configuraciones de FVG creadas utilizando la función de bucle for.

   for (int i=ArraySize(totalFVGs)-1; i>=0; i--){// ... }

Obtener/recuperar los datos de configuración.

      string objName = totalFVGs[i];
      string fvgNAME = ObjectGetString(0,objName,OBJPROP_NAME);
      int barIndex = barINDICES[i];
      datetime timeSTART = (datetime)ObjectGetInteger(0,fvgNAME,OBJPROP_TIME,0);
      datetime timeEND = (datetime)ObjectGetInteger(0,fvgNAME,OBJPROP_TIME,1);
      double fvgLOW = ObjectGetDouble(0,fvgNAME,OBJPROP_PRICE,0);
      double fvgHIGH = ObjectGetDouble(0,fvgNAME,OBJPROP_PRICE,1);
      color fvgColor = (color)ObjectGetInteger(0,fvgNAME,OBJPROP_COLOR);

Recorra en bucle todas las barras de extensión del rectángulo, es decir, la longitud del rectángulo seleccionado.

      for (int k=barIndex-1; k>=(barIndex-FVG_Rec_Ext_Bars); k--){//... }

Obtenga los datos de la barra.

         datetime barTime = iTime(_Symbol,_Period,k);
         double barLow = iLow(_Symbol,_Period,k);
         double barHigh = iHigh(_Symbol,_Period,k);

En el caso de que el rectángulo se extienda más allá de la primera barra visible de una serie temporal, basta con truncarlo hasta la hora de la primera barra, es decir, la barra del índice 0, o dicho de otro modo, la barra actual. Informa de la detección de desbordamiento, actualiza la configuración de la FVG al tiempo de la barra actual y rompe el bucle de funcionamiento.

         if (k==0){
            Print("OverFlow Detected @ fvg ",fvgNAME);
            UpdateRec(fvgNAME,timeSTART,fvgLOW,barTime,fvgHIGH);
            break;
         }

Para actualizar fácilmente las configuraciones, creamos una función void con el nombre UpdateRec, pasando el nombre del objeto y las coordenadas de los dos puntos vitales del objeto rectángulo que hay que actualizar.

void UpdateRec(string objName,datetime time1,double price1,
               datetime time2, double price2){
   if (ObjectFind(0,objName) >= 0){
      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);
      
      ChartRedraw(0);
   }
}

En el caso de que la configuración de la FVG sea una FVG bajista, detectada por el color, y el precio máximo de la barra seleccionada esté por encima de las coordenadas superiores del objeto FVG, señala que la FVG fue un éxito y, por lo tanto, la trunca en consecuencia, es decir, actualiza las coordenadas del segundo punto a la vela en la que se produce la ruptura de la FVG. Del mismo modo, se mantiene una configuración alcista de la FVG.

         if ((fvgColor == CLR_DOWN && barHigh > fvgHIGH) ||
            (fvgColor == CLR_UP && barLow < fvgLOW)
         ){
            Print("Cut Off @ bar no: ",k," of Time: ",barTime);
            UpdateRec(fvgNAME,timeSTART,fvgLOW,barTime,fvgHIGH);
            break;
         }


Una vez que todo está listo, redimensionamos las matrices de almacenamiento a cero, para prepararnos para la sección OnTick.

   ArrayResize(totalFVGs,0);
   ArrayResize(barINDICES,0);

El código completo OnInit responsable de crear las configuraciones FVG en las barras visibles del gráfico y de actualizarlas en consecuencia será el siguiente:

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

   int visibleBars = (int)ChartGetInteger(0,CHART_VISIBLE_BARS);
   Print("Total visible bars on chart = ",visibleBars);
   
   if (ObjectsTotal(0,0,OBJ_RECTANGLE)==0){
      Print("No FVGs Found, Resizing storage arrays to 0 now!!!");
      ArrayResize(totalFVGs,0);
      ArrayResize(barINDICES,0);
      ArrayResize(signalFVGs,0);
   }
   
   ObjectsDeleteAll(0,FVG_Prefix);
   
   for (int i=0; i<=visibleBars; i++){
      //Print("Bar Index = ",i);
      double low0 = iLow(_Symbol,_Period,i);
      double high2 = iHigh(_Symbol,_Period,i+2);
      double gap_L0_H2 = NormalizeDouble((low0 - high2)/_Point,_Digits);
      
      double high0 = iHigh(_Symbol,_Period,i);
      double low2 = iLow(_Symbol,_Period,i+2);
      double gap_H0_L2 = NormalizeDouble((low2 - high0)/_Point,_Digits);
      
      bool FVG_UP = low0 > high2 && gap_L0_H2 > minPts;
      bool FVG_DOWN = low2 > high0 && gap_H0_L2 > minPts;
      
      if (FVG_UP || FVG_DOWN){
         Print("Bar Index with FVG = ",i+1);
         datetime time1 = iTime(_Symbol,_Period,i+1);
         double price1 = FVG_UP ? high2 : high0;
         datetime time2 = time1 + PeriodSeconds(_Period)*FVG_Rec_Ext_Bars;
         double price2 = FVG_UP ? low0 : low2;
         string fvgNAME = FVG_Prefix+"("+TimeToString(time1)+")";
         color fvgClr = FVG_UP ? CLR_UP : CLR_DOWN;
         CreateRec(fvgNAME,time1,price1,time2,price2,fvgClr);
         Print("Old ArraySize = ",ArraySize(totalFVGs));
         ArrayResize(totalFVGs,ArraySize(totalFVGs)+1);
         ArrayResize(barINDICES,ArraySize(barINDICES)+1);
         Print("New ArraySize = ",ArraySize(totalFVGs));
         totalFVGs[ArraySize(totalFVGs)-1] = fvgNAME;
         barINDICES[ArraySize(barINDICES)-1] = i+1;
         ArrayPrint(totalFVGs);
         ArrayPrint(barINDICES);
      }
   }
   
   for (int i=ArraySize(totalFVGs)-1; i>=0; i--){
      string objName = totalFVGs[i];
      string fvgNAME = ObjectGetString(0,objName,OBJPROP_NAME);
      int barIndex = barINDICES[i];
      datetime timeSTART = (datetime)ObjectGetInteger(0,fvgNAME,OBJPROP_TIME,0);
      datetime timeEND = (datetime)ObjectGetInteger(0,fvgNAME,OBJPROP_TIME,1);
      double fvgLOW = ObjectGetDouble(0,fvgNAME,OBJPROP_PRICE,0);
      double fvgHIGH = ObjectGetDouble(0,fvgNAME,OBJPROP_PRICE,1);
      color fvgColor = (color)ObjectGetInteger(0,fvgNAME,OBJPROP_COLOR);
      
      Print("FVG NAME = ",fvgNAME," >No: ",barIndex," TS: ",timeSTART," TE: ",
            timeEND," LOW: ",fvgLOW," HIGH: ",fvgHIGH," CLR = ",fvgColor);
      for (int k=barIndex-1; k>=(barIndex-FVG_Rec_Ext_Bars); k--){
         datetime barTime = iTime(_Symbol,_Period,k);
         double barLow = iLow(_Symbol,_Period,k);
         double barHigh = iHigh(_Symbol,_Period,k);
         //Print("Bar No: ",k," >Time: ",barTime," >H: ",barHigh," >L: ",barLow);
         
         if (k==0){
            Print("OverFlow Detected @ fvg ",fvgNAME);
            UpdateRec(fvgNAME,timeSTART,fvgLOW,barTime,fvgHIGH);
            break;
         }
         
         if ((fvgColor == CLR_DOWN && barHigh > fvgHIGH) ||
            (fvgColor == CLR_UP && barLow < fvgLOW)
         ){
            Print("Cut Off @ bar no: ",k," of Time: ",barTime);
            UpdateRec(fvgNAME,timeSTART,fvgLOW,barTime,fvgHIGH);
            break;
         }
      }
      
   }
   
   ArrayResize(totalFVGs,0);
   ArrayResize(barINDICES,0);

   return(INIT_SUCCEEDED);
}

En la sección OnTick, se utilizan funciones y lógica similares. Hacemos un bucle a través de todas las barras de extensión previas predefinidas, o más bien la longitud de la configuración de la FVG desde la barra actual, buscamos configuraciones potenciales y creamos si se encuentra y confirma alguna. 

He aquí las ligeras diferencias en el bucle de creación utilizado.

En el bucle de creación de la configuración FVG OnInit, consideramos todas las barras visibles del gráfico. Aquí, por el contrario, sólo consideramos la última longitud de extensión predefinida.

   for (int i=0; i<=FVG_Rec_Ext_Bars; i++){//... }

Cuando obtenemos información de las barras, empezamos en la barra anterior al índice de la barra actual, ya que la barra aún está en proceso de formación. Así que añadimos 1 al índice seleccionado.

      double low0 = iLow(_Symbol,_Period,i+1);
      double high2 = iHigh(_Symbol,_Period,i+2+1);

En el bucle de truncamiento OnInit FVG, truncamos las configuraciones FVG confirmadas y probadas. En la sección OnTick, no truncamos las configuraciones porque queremos verlas. En su lugar, enviamos órdenes de mercado instantáneas respectivamente. De nuevo, una vez que el precio supera la longitud de la barra, significa que no podemos operar con esa configuración en absoluto, por lo que nos deshacemos de los datos almacenados en las matrices. He aquí las diferencias de código:

Añadimos una nueva variable booleana fvgExist y la inicializamos a false, para que pueda contener las banderas sobre la disponibilidad de una configuración dentro de las barras escaneadas.

      bool fvgExist = false;

Volvemos a hacer un bucle a través de las barras predefinidas comenzando por la barra anterior a la barra actual, es decir, 0 + 1 = 1, obteniendo los precios máximo y mínimo de la barra seleccionada, y si alguno de los precios coincide con las coordenadas del segundo punto de la configuración, todavía está dentro del rango, por lo tanto todavía se puede negociar, y por lo tanto establecemos la variable fvgExist en true.

      for (int k=1; k<=FVG_Rec_Ext_Bars; k++){
         double barLow = iLow(_Symbol,_Period,k);
         double barHigh = iHigh(_Symbol,_Period,k);
         
         if (barHigh == fvgLow || barLow == fvgLow){
            //Print("Found: ",fvgNAME," @ bar ",k);
            fvgExist = true;
            break;
         }
      }

Puesto que queremos infiltrarnos en el mercado, y para ello necesitaremos los niveles de precios de entrada, definimos las cotizaciones del símbolo actual con antelación.

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

A continuación, utilice las sentencias if para comprobar si la configuración FVG seleccionada es una configuración bajista y que el precio Bid actual está por encima de las coordenadas superiores de la configuración y, al mismo tiempo, se trata de la primera señal de operación para la configuración, y cuando se superen todas las condiciones, abra una orden de venta, con el volumen de operación de 0,01. El precio de apertura es el precio de oferta actual del mercado, con el nivel de toma de beneficios en las coordenadas inferiores de la configuración de la FVG, y el stop loss en una relación riesgo/beneficio de 1:10, por encima del precio de entrada. Una vez iniciada la posición, establecemos los datos de la señal en ese índice concreto en verdadero, para no abrir ninguna otra posición basada en esa configuración concreta en el siguiente tick.

      if (fvgColor == CLR_DOWN && Bid > fvgHigh && !signalFVGs[j]){
         Print("SELL SIGNAL For (",fvgNAME,") Now @ ",Bid);
         double SL_sell = Ask + NormalizeDouble((((fvgHigh-fvgLow)/_Point)*10)*_Point,_Digits);
         double trade_lots = Check1_ValidateVolume_Lots(0.01);
         
         if (Check2_Margin(ORDER_TYPE_SELL,trade_lots) &&
             Check3_VolumeLimit(trade_lots) &&
             Check4_TradeLevels(POSITION_TYPE_SELL,SL_sell,fvgLow)){
            obj_Trade.Sell(trade_lots,_Symbol,Bid,SL_sell,fvgLow);
            signalFVGs[j] = true;
         }
         ArrayPrint(totalFVGs,_Digits," [< >] ");
         ArrayPrint(signalFVGs,_Digits," [< >] ");
      }


Del mismo modo, se mantiene una confirmación alcista, pero prevalecen las condiciones opuestas.

      else if (fvgColor == CLR_UP && Ask < fvgLow && !signalFVGs[j]){
         Print("BUY SIGNAL For (",fvgNAME,") Now @ ",Ask);
         double SL_buy = Bid - NormalizeDouble((((fvgHigh-fvgLow)/_Point)*10)*_Point,_Digits);
         double trade_lots = Check1_ValidateVolume_Lots(0.01);

         if (Check2_Margin(ORDER_TYPE_BUY,trade_lots) &&
             Check3_VolumeLimit(trade_lots) &&
             Check4_TradeLevels(POSITION_TYPE_BUY,SL_buy,fvgHigh)){
            obj_Trade.Buy(trade_lots,_Symbol,Ask,SL_buy,fvgHigh);
            signalFVGs[j] = true;
         }
         ArrayPrint(totalFVGs,_Digits," [< >] ");
         ArrayPrint(signalFVGs,_Digits," [< >] ");
      }


Por último, una vez que la configuración FVG desaparece, significa que ya no podemos comerciar con ella, por lo que nuestra preocupación es menor. Así que liberamos las matrices de almacenamiento deshaciéndonos de los datos correspondientes. Esto se consigue utilizando la función ArrayRemove, pasando en el array, la posición inicial, en este caso 0 para el primer dato, y el total de elementos, en este caso 1, ya que sólo queremos eliminar los datos de esa única y actualmente seleccionada configuración de la FVG.

      if (fvgExist == false){
         bool removeName = ArrayRemove(totalFVGs,0,1);
         bool removeTime = ArrayRemove(barTIMEs,0,1);
         bool removeSignal = ArrayRemove(signalFVGs,0,1);
         if (removeName && removeTime && removeSignal){
            Print("Success removing the FVG DATA from the arrays. New Data as Below:");
            Print("FVGs: ",ArraySize(totalFVGs)," TIMEs: ",ArraySize(barTIMEs),
                     " SIGNALs: ",ArraySize(signalFVGs));
            ArrayPrint(totalFVGs);
            ArrayPrint(barTIMEs);
            ArrayPrint(signalFVGs);
         }
      }


A continuación se muestra el código OnTick necesario para crear las configuraciones FVG, negociar las configuraciones confirmadas y eliminar los datos de las que están fuera de los límites de las matrices de almacenamiento:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
   
   for (int i=0; i<=FVG_Rec_Ext_Bars; i++){
      double low0 = iLow(_Symbol,_Period,i+1);
      double high2 = iHigh(_Symbol,_Period,i+2+1);
      double gap_L0_H2 = NormalizeDouble((low0 - high2)/_Point,_Digits);
      
      double high0 = iHigh(_Symbol,_Period,i+1);
      double low2 = iLow(_Symbol,_Period,i+2+1);
      double gap_H0_L2 = NormalizeDouble((low2 - high0)/_Point,_Digits);
      
      bool FVG_UP = low0 > high2 && gap_L0_H2 > minPts;
      bool FVG_DOWN = low2 > high0 && gap_H0_L2 > minPts;
      
      if (FVG_UP || FVG_DOWN){
         datetime time1 = iTime(_Symbol,_Period,i+1+1);
         double price1 = FVG_UP ? high2 : high0;
         datetime time2 = time1 + PeriodSeconds(_Period)*FVG_Rec_Ext_Bars;
         double price2 = FVG_UP ? low0 : low2;
         string fvgNAME = FVG_Prefix+"("+TimeToString(time1)+")";
         color fvgClr = FVG_UP ? CLR_UP : CLR_DOWN;
         
         if (ObjectFind(0,fvgNAME) < 0){
            CreateRec(fvgNAME,time1,price1,time2,price2,fvgClr);
            Print("Old ArraySize = ",ArraySize(totalFVGs));
            ArrayResize(totalFVGs,ArraySize(totalFVGs)+1);
            ArrayResize(barTIMEs,ArraySize(barTIMEs)+1);
            ArrayResize(signalFVGs,ArraySize(signalFVGs)+1);
            Print("New ArraySize = ",ArraySize(totalFVGs));
            totalFVGs[ArraySize(totalFVGs)-1] = fvgNAME;
            barTIMEs[ArraySize(barTIMEs)-1] = time1;
            signalFVGs[ArraySize(signalFVGs)-1] = false;
            ArrayPrint(totalFVGs);
            ArrayPrint(barTIMEs);
            ArrayPrint(signalFVGs);
         }
      }
   }
   
   for (int j=ArraySize(totalFVGs)-1; j>=0; j--){
      bool fvgExist = false;
      string objName = totalFVGs[j];
      string fvgNAME = ObjectGetString(0,objName,OBJPROP_NAME);
      double fvgLow = ObjectGetDouble(0,fvgNAME,OBJPROP_PRICE,0);
      double fvgHigh = ObjectGetDouble(0,fvgNAME,OBJPROP_PRICE,1);
      color fvgColor = (color)ObjectGetInteger(0,fvgNAME,OBJPROP_COLOR);
      
      for (int k=1; k<=FVG_Rec_Ext_Bars; k++){
         double barLow = iLow(_Symbol,_Period,k);
         double barHigh = iHigh(_Symbol,_Period,k);
         
         if (barHigh == fvgLow || barLow == fvgLow){
            //Print("Found: ",fvgNAME," @ bar ",k);
            fvgExist = true;
            break;
         }
      }
      
      //Print("Existence of ",fvgNAME," = ",fvgExist);
      
      double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
      double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
      
      if (fvgColor == CLR_DOWN && Bid > fvgHigh && !signalFVGs[j]){
         Print("SELL SIGNAL For (",fvgNAME,") Now @ ",Bid);
         double SL_sell = Ask + NormalizeDouble((((fvgHigh-fvgLow)/_Point)*10)*_Point,_Digits);
         double trade_lots = Check1_ValidateVolume_Lots(0.01);
         
         if (Check2_Margin(ORDER_TYPE_SELL,trade_lots) &&
             Check3_VolumeLimit(trade_lots) &&
             Check4_TradeLevels(POSITION_TYPE_SELL,SL_sell,fvgLow)){
            obj_Trade.Sell(trade_lots,_Symbol,Bid,SL_sell,fvgLow);
            signalFVGs[j] = true;
         }
         ArrayPrint(totalFVGs,_Digits," [< >] ");
         ArrayPrint(signalFVGs,_Digits," [< >] ");
      }
      else if (fvgColor == CLR_UP && Ask < fvgLow && !signalFVGs[j]){
         Print("BUY SIGNAL For (",fvgNAME,") Now @ ",Ask);
         double SL_buy = Bid - NormalizeDouble((((fvgHigh-fvgLow)/_Point)*10)*_Point,_Digits);
         double trade_lots = Check1_ValidateVolume_Lots(0.01);

         if (Check2_Margin(ORDER_TYPE_BUY,trade_lots) &&
             Check3_VolumeLimit(trade_lots) &&
             Check4_TradeLevels(POSITION_TYPE_BUY,SL_buy,fvgHigh)){
            obj_Trade.Buy(trade_lots,_Symbol,Ask,SL_buy,fvgHigh);
            signalFVGs[j] = true;
         }
         ArrayPrint(totalFVGs,_Digits," [< >] ");
         ArrayPrint(signalFVGs,_Digits," [< >] ");
      }
      
      if (fvgExist == false){
         bool removeName = ArrayRemove(totalFVGs,0,1);
         bool removeTime = ArrayRemove(barTIMEs,0,1);
         bool removeSignal = ArrayRemove(signalFVGs,0,1);
         if (removeName && removeTime && removeSignal){
            Print("Success removing the FVG DATA from the arrays. New Data as Below:");
            Print("FVGs: ",ArraySize(totalFVGs)," TIMEs: ",ArraySize(barTIMEs),
                     " SIGNALs: ",ArraySize(signalFVGs));
            ArrayPrint(totalFVGs);
            ArrayPrint(barTIMEs);
            ArrayPrint(signalFVGs);
         }
      }      
   }
   
}

¡Bien por nosotros! Ahora sí hemos creado un sistema de negociación con un concepto de dinero inteligente basado en la estrategia FVG/Desequilibrio para generar señales de negociación.


Resultados del probador de estrategias de la brecha del valor justo (FVG)

Tras probarlo en el probador de estrategias, estos son los resultados.

  • Gráfico Balance/Equidad neto


  • Resultados de las pruebas retrospectivas:



Conclusión

En conclusión, la exploración de la codificación de la brecha del valor justo o estrategia de desequilibrio a través de los diversos temas descritos proporciona valiosos conocimientos sobre las metodologías de negociación cuantitativa. En este artículo nos hemos adentrado en los intrincados detalles de la aplicación de tales estrategias, tocando componentes clave como el análisis de datos, los modelos estadísticos y las técnicas de negociación algorítmica.

En primer lugar, es esencial comprender el concepto de brecha o desequilibrio del valor justo, ya que constituye la base de la estrategia. Consiste en identificar las discrepancias entre el precio de mercado de un activo y su valor intrínseco y utilizar métodos estadísticos para calibrar estas diferencias con precisión.

Además, el artículo hace hincapié en la importancia de unas técnicas sólidas de análisis de datos para extraer perspectivas significativas de los datos financieros. Técnicas como el análisis de series temporales, el análisis de velas y el análisis del sentimiento desempeñan un papel crucial en la identificación de patrones y tendencias que pueden informar las decisiones de negociación.

Además, el aspecto de codificación de la estrategia destaca la importancia de dominar el lenguaje de programación MQL5, así como de familiarizarse con las bibliotecas y funciones MQL5. Las prácticas de codificación eficaces permiten automatizar los procesos de negociación, lo que facilita 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. Sólo pretende mostrar ideas sobre cómo crear un EA de brecha de valor justo basado en el enfoque del concepto de dinero inteligente y, por lo tanto, debería utilizarse como base para crear un asesor experto mejor con más optimización y extracción de datos tenidos en cuenta. La información presentada no garantiza ningún resultado comercial. 

En general, el artículo subraya la naturaleza interdisciplinar del comercio cuantitativo, la estadística y el análisis de barras para desarrollar estrategias eficaces que permitan navegar por unos mercados financieros dinámicos. Al sintetizar los conceptos teóricos con las implementaciones prácticas de codificación, el artículo dota a los lectores de las herramientas necesarias para llevar a cabo con éxito las operaciones cuantitativas, en concreto el enfoque del concepto de dinero inteligente (Smart Money Concept, SMC).


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

Archivos adjuntos |
FVG_SMC_EA.mq5 (21.23 KB)
DoEasy. Funciones de servicio (Parte 2): Patrón "Barra interior" DoEasy. Funciones de servicio (Parte 2): Patrón "Barra interior"
En este artículo, continuaremos el análisis de los patrones de precios en la biblioteca DoEasy. Así, crearemos la clase de patrón "Barra interior" de las formaciones Price Action.
Creación de un modelo de restricción de tendencia de velas (Parte 2): Fusionar indicadores nativos Creación de un modelo de restricción de tendencia de velas (Parte 2): Fusionar indicadores nativos
Este artículo se centra en el aprovechamiento de los indicadores MetaTrader 5 incorporados para filtrar las señales fuera de tendencia. Avanzando desde el artículo anterior exploraremos cómo hacerlo utilizando código MQL5 para comunicar nuestra idea al programa final.
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.
Algoritmos de optimización de la población: Algoritmo de optimización de ballenas (Whale Optimization Algorithm, WOA) Algoritmos de optimización de la población: Algoritmo de optimización de ballenas (Whale Optimization Algorithm, WOA)
El algoritmo de optimización de ballenas (WOA) es un algoritmo metaheurístico inspirado en el comportamiento y las estrategias de caza de las ballenas jorobadas. La idea básica del WOA es imitar el método de alimentación denominado "red de burbujas", en el que las ballenas crean burbujas alrededor de la presa para atacarla después en espiral.