La estrategia de negociación de la brecha del valor razonable inverso (Inverse Fair Value Gap, IFVG)
Introducción
Una brecha inversa del valor razonable (Inverse Fair Value Gap, IFVG) se produce cuando el precio vuelve a una brecha del valor razonable identificada previamente y, en lugar de mostrar la reacción de apoyo o resistencia esperada, no la respeta. Este comportamiento puede indicar un posible cambio en la dirección del mercado y ofrecer una ventaja comercial contraria. En este artículo, voy a presentar mi enfoque, desarrollado por mí mismo, para cuantificar y utilizar la brecha inversa del valor razonable como estrategia para los asesores expertos de MetaTrader 5.
La motivación de la estratégia
Comprender las diferencias entre el valor razonable y el valor de mercado (Fair Value Gaps, FVG)
Para apreciar plenamente la intuición que subyace a una «brecha de valor razonable inversa», es útil partir de lo que representa una brecha de valor razonable estándar (FVG). Una brecha de valor razonable se define normalmente dentro de un patrón de precios de tres velas.

Un FVG se produce cuando el cuerpo de la vela B (y a menudo las mechas) lanza el precio de mercado bruscamente al alza o a la baja, de tal manera que queda un «hueco» detrás. Más concretamente, si el mínimo de la vela C es superior al máximo de la vela A en un fuerte movimiento alcista, el espacio entre estos dos puntos de precio se considera una brecha de valor justo. Esta brecha refleja una zona de ineficiencia o desequilibrio en el mercado; un área donde las operaciones no contaron con una participación bilateral adecuada porque el precio se movió demasiado rápido en una dirección. Los operadores suelen asumir que el flujo de órdenes institucionales causó este desplazamiento, dejando “huellas” de la actividad de grandes capitales.
La lógica común es que el precio, en algún momento, suele volver a esos huecos para “rellenarlos”. El cierre de esta brecha puede considerarse como la forma que tiene el mercado de equilibrar el flujo de órdenes que anteriormente era unilateral. Los operadores que siguen este principio suelen esperar a que el precio vuelva a visitar este hueco, buscando una reacción que confirme la continuación en la dirección original, o a veces una reversión.
¿Qué es una brecha inversa de valor razonable?
El concepto de “brecha inversa del valor razonable” se basa en esta idea, pero la aborda desde una perspectiva contraria o de ingeniería inversa. En lugar de utilizar la brecha de valor razonable como una zona para confirmar la continuación en la dirección original, una estrategia FVG inversa podría utilizar esa misma brecha para anticipar dónde el mercado podría no seguir la tendencia y posiblemente revertirla.
Por ejemplo, para distinguir una brecha inversa de valor razonable bajista, se pueden seguir estos pasos:
- Identificar un FVG alcista.
- El precio vuelve a la zona FVG.
- En lugar de respetarlo como un soporte, observa cómo se comporta el precio. Si no logra impulsarse al alza y, en cambio, opera a través del hueco como si no proporcionara un soporte significativo, ese fracaso podría indicar un cambio de impulso.
- Abra posiciones cortas, anticipando que la incapacidad de utilizar el FVG como trampolín para precios más altos significa que el mercado podría ahora dirigirse a la baja.
La intuición detrás de las brechas inversas de valor razonable
- Huellas institucionales y puntos débiles: La hipótesis subyacente tras las diferencias en el valor razonable es que los grandes actores sofisticados crearon el desequilibrio inicial. Cuando el precio regresa a estas zonas, a menudo se pone a prueba: si los grandes inversores siguen viendo valor en estos precios, sus órdenes pendientes pueden sostener o resistir el precio, provocando una reacción. Si, en cambio, el precio atraviesa directamente el FVG sin un fuerte rebote o continuación, sugiere que esas grandes órdenes podrían haberse ejecutado, cancelado o ya no están defendiendo esa zona. Esto puede indicar un cambio en la intención del mercado.
- Detectar debilidad o fortaleza de forma temprana: Al centrarse en lo que no ocurre cuando el precio vuelve a la brecha, los operadores pueden obtener pistas sutiles sobre la fortaleza o debilidad subyacente. Si un mercado alcista no logra obtener un impulso de una zona de ineficiencia conocida (el FVG alcista), esto podría ser una señal de alerta temprana de que la narrativa alcista podría estar perdiendo fuerza.
- Complemento de las estrategias tradicionales de FVG: Las estrategias tradicionales de FVG se basan en la hipótesis de un reequilibrio seguido de una continuación en la dirección original. Sin embargo, los mercados son dinámicos y no todas las recuperaciones de huecos conducen a la reanudación de la tendencia anterior. El enfoque FVG inverso puede brindar al operador una “ventaja” adicional al identificar situaciones en las que la estrategia habitual falla, y por lo tanto, una jugada contraria puede tener mayor probabilidad y mejor relación riesgo/recompensa.
El concepto de brechas inversas de valor razonable se basa en el reconocimiento de que los mercados están constantemente poniendo a prueba y volviendo a probar áreas de desequilibrio previo. Mientras que el trading tradicional de FVG se centra en el reequilibrio y la continuidad exitosos, el enfoque inverso obtiene una ventaja al identificar cuándo este proceso de reequilibrio no produce el resultado esperado. Este cambio de perspectiva convierte lo que podría haber sido una oportunidad perdida, o incluso una propuesta perdedora, en una configuración contraria con una ventaja potencialmente alta. En un entorno de mercado donde anticiparse a lo inesperado a menudo se recompensa, el concepto de FVG inverso añade una herramienta adicional al arsenal de técnicas de análisis técnico del operador.
Desarrollo de la estrategia
De forma similar a como los operadores discrecionales utilizan las brechas de valor razonable, las brechas de valor razonable inversas también se negocian activamente debido a los sofisticados criterios necesarios para identificar patrones válidos. Operar con cada brecha de valor razonable inverso sin discriminación probablemente resultaría en un rendimiento aleatorio, ya que la mayoría de las brechas no se alinean con la intuición estratégica que comenté anteriormente. Con el fin de cuantificar las configuraciones de características que consideran los operadores discrecionales, realicé extensas pruebas de características y establecí las siguientes reglas:
-
Alineación con la tendencia macroeconómica: El precio debería seguir la tendencia macroeconómica general, que viene determinada por su posición relativa con respecto a la media móvil de 400 períodos.
-
Selección del período de tiempo adecuado: Debe utilizarse un intervalo de tiempo corto, como de 1 a 5 minutos, porque el concepto de "completar pedidos" se produce en un breve lapso de tiempo. Para los fines de este artículo, se utiliza un lapso de tiempo de 3 minutos.
-
Centrarse en la brecha de valor razonable más reciente: Solo se considera la brecha de valor razonable (FVG) más reciente, ya que es la que tiene mayor relevancia para reflejar las condiciones actuales del mercado.
-
Validación del tamaño de la brecha de valor razonable: El FVG no debe ser ni demasiado grande ni demasiado pequeño en comparación con las velas circundantes. Una brecha demasiado pequeña carece de la importancia suficiente para actuar como un nivel de soporte o resistencia fiable, mientras que una brecha demasiado grande probablemente se deba a un acontecimiento noticioso, que puede retrasar la señal de reversión. Para garantizar que el FVG sea significativo, se establecen umbrales específicos para validar cada brecha.
-
Tamaño de vela de ruptura controlada: Del mismo modo, la vela de ruptura no debería ser excesivamente grande, ya que las entradas se basan en los cierres de las velas. Las grandes velas de ruptura pueden generar señales tardías, algo que la estrategia pretende evitar.
-
Reversión de precios y ruptura oportunas: Dentro de un plazo determinado tras la formación de un FVG, el precio debe revertir hasta el hueco y romper desde el borde opuesto con una vela cerrada. Esto se logra examinando únicamente los FVG más recientes dentro de un breve período de análisis retrospectivo.
-
Confirmación de la fuerza de ruptura: El FVG debe alinearse con un nivel de rechazo previo, asegurando que una ruptura del FVG indique una mayor fuerza en la dirección correspondiente.
Ahora, vamos a analizar el código.
En primer lugar, declaramos las variables globales necesarias. Estas variables globales contienen datos clave para el seguimiento de las brechas de valor razonable (FVG), las operaciones abiertas actuales y el estado del sistema. Variables como previousGapHigh, previousGapLow y lastGapIndex ayudan a realizar un seguimiento del gap identificado más reciente. handleMa almacenará el identificador de la media móvil. Las variables buypos y sellpos realizan un seguimiento de las órdenes de negociación abiertas, mientras que currentFVGstatus y newFVGformed realizan un seguimiento del estado del último FVG identificado.
string previousGapObjName = ""; double previousGapHigh = 0.0; double previousGapLow = 0.0; int LastGapIndex = 0; double gapHigh = 0.0; double gapLow = 0.0; double gap = 0.0; double lott= 0.1; ulong buypos = 0, sellpos = 0; double anyGap = 0.0; double anyGapHigh = 0.0; double anyGapLow = 0.0; int barsTotal = 0; int newFVGformed = 0; int currentFVGstatus = 0; int handleMa; #include <Trade/Trade.mqh> CTrade trade;
A continuación, declaramos las siguientes funciones para ejecutar operaciones con toma de ganancias y límite de pérdidas, y para realizar un seguimiento del ticket de orden para cada operación.
//+------------------------------------------------------------------+ //| Store order ticket number into buypos/sellpos variables | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { if (trans.type == TRADE_TRANSACTION_ORDER_ADD) { COrderInfo order; if (order.Select(trans.order)) { if (order.Magic() == Magic) { if (order.OrderType() == ORDER_TYPE_BUY) { buypos = order.Ticket(); } else if (order.OrderType() == ORDER_TYPE_SELL) { sellpos = order.Ticket(); } } } } } //+------------------------------------------------------------------+ //| Execute sell trade function | //+------------------------------------------------------------------+ void executeSell() { if (IsWithinTradingHours()){ double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); bid = NormalizeDouble(bid,_Digits); double tp = bid - tpp * _Point; tp = NormalizeDouble(tp, _Digits); double sl = bid + slp * _Point; sl = NormalizeDouble(sl, _Digits); trade.Sell(lott,_Symbol,bid,sl,tp); sellpos = trade.ResultOrder(); } } //+------------------------------------------------------------------+ //| Execute buy trade function | //+------------------------------------------------------------------+ void executeBuy() { if (IsWithinTradingHours()){ double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); ask = NormalizeDouble(ask,_Digits); double tp = ask + tpp * _Point; tp = NormalizeDouble(tp, _Digits); double sl = ask - slp * _Point; sl = NormalizeDouble(sl, _Digits); trade.Buy(lott,_Symbol,ask,sl,tp); buypos= trade.ResultOrder(); } } //+------------------------------------------------------------------+ //| Check if is trading hours | //+------------------------------------------------------------------+ bool IsWithinTradingHours() { datetime currentTime = TimeTradeServer(); MqlDateTime timeStruct; TimeToStruct(currentTime, timeStruct); int currentHour = timeStruct.hour; if (currentHour >= startHour && currentHour < endHour) return true; else return false; }
Luego, utilizamos estas dos funciones para validar una brecha de valor razonable. IsReacted() comprueba que, dentro del período de análisis, haya al menos dos mechas de velas dentro del rango de precios del FVG actual, lo que interpretamos como una señal de rechazo previo del FVG. La función IsGapValid() verifica entonces que el tamaño del espacio se encuentre dentro del rango deseado, devolviendo verdadero o falso.
//+------------------------------------------------------------------+ //| Function to validate the FVG gap | //+------------------------------------------------------------------+ bool IsGapValid(){ if (anyGap<=gapMaxPoint*_Point && anyGap>=gapMinPoint*_Point&&IsReacted()) return true; else return false; } //+------------------------------------------------------------------+ //| Check for gap reaction to validate its strength | //+------------------------------------------------------------------+ bool IsReacted(){ int count1 = 0; int count2 = 0; for (int i = 4; i < lookBack; i++){ double aLow = iLow(_Symbol,PERIOD_CURRENT,i); double aHigh = iHigh(_Symbol,PERIOD_CURRENT,i); if (aHigh<anyGapHigh&&aHigh>anyGapLow&&aLow<anyGapLow){ count1++; } else if (aLow<anyGapHigh&&aLow>anyGapLow&&aHigh>anyGapHigh){ count2++; } } if (count1>=2||count2>=2) return true; else return false; }
Después de eso, utilizamos estas funciones para comprobar si actualmente hay una ruptura en el último FVG.
//+------------------------------------------------------------------+ //| Check if price broke out to the upside of the gap | //+------------------------------------------------------------------+ bool IsBrokenUp(){ int lastClosedIndex = 1; double lastOpen = iOpen(_Symbol, PERIOD_CURRENT, lastClosedIndex); double lastClose = iClose(_Symbol, PERIOD_CURRENT, lastClosedIndex); if (lastOpen < gapHigh && lastClose > gapHigh&&(lastClose-gapHigh)<maxBreakoutPoints*_Point) { if(currentFVGstatus==-1){ return true;} } return false; } //+------------------------------------------------------------------+ //| Check if price broke out to the downside of the gap | //+------------------------------------------------------------------+ bool IsBrokenLow(){ int lastClosedIndex = 1; double lastOpen = iOpen(_Symbol, PERIOD_CURRENT, lastClosedIndex); double lastClose = iClose(_Symbol, PERIOD_CURRENT, lastClosedIndex); if (lastOpen > gapLow && lastClose < gapLow&&(gapLow -lastClose)<maxBreakoutPoints*_Point) { if(currentFVGstatus==1){ return true;} } return false; }
Finalmente, utilizamos estas dos funciones para comprobar la validez del hueco con IsGapValid() y, si es válido, actualiza las variables globales, marca el FVG como nuevo y lo dibuja en el gráfico. La función getFVG() es esencial para codificar toda la estrategia. Lo llamamos en cada nuevo bar para comprobar si hay un FVG válido. Si el FVG es válido, verificamos si es diferente del último que guardamos y, de ser así, lo guardamos en la variable global para actualizar el estado.
//+------------------------------------------------------------------+ //| To get the most recent Fair Value Gap (FVG) | //+------------------------------------------------------------------+ void getFVG() { // Loop through the bars to find the most recent FVG for (int i = 1; i < 3; i++) { datetime currentTime = iTime(_Symbol,PERIOD_CURRENT, i); datetime previousTime = iTime(_Symbol,PERIOD_CURRENT, i + 2); // Get the high and low of the current and previous bars double currentLow = iLow(_Symbol,PERIOD_CURRENT, i); double previousHigh = iHigh(_Symbol,PERIOD_CURRENT, i+2); double currentHigh = iHigh(_Symbol,PERIOD_CURRENT, i); double previousLow = iLow(_Symbol,PERIOD_CURRENT, i+2); anyGap = MathAbs(previousLow - currentHigh); // Check for an upward gap if (currentLow > previousHigh) { anyGapHigh = currentLow; anyGapLow = previousHigh; //Check for singular if (LastGapIndex != i){ if (IsGapValid()){ gapHigh = currentLow; gapLow = previousHigh; gap = anyGap; currentFVGstatus = 1;//bullish FVG DrawGap(previousTime,currentTime,gapHigh,gapLow); LastGapIndex = i; newFVGformed =1; return; } } } // Check for a downward gap else if (currentHigh < previousLow) { anyGapHigh = previousLow; anyGapLow = currentHigh; if (LastGapIndex != i){ if(IsGapValid()){ gapHigh = previousLow; gapLow = currentHigh; gap = anyGap; currentFVGstatus = -1; DrawGap(previousTime,currentTime,gapHigh,gapLow); LastGapIndex = i; newFVGformed =1; return; } } } } } //+------------------------------------------------------------------+ //| Function to draw the FVG gap on the chart | //+------------------------------------------------------------------+ void DrawGap(datetime timeStart, datetime timeEnd, double gaphigh, double gaplow) { // Delete the previous gap object if it exists if (previousGapObjName != "") { ObjectDelete(0, previousGapObjName); } // Generate a new name for the gap object previousGapObjName = "FVG_" + IntegerToString(TimeCurrent()); // Create a rectangle object to highlight the gap ObjectCreate(0, previousGapObjName, OBJ_RECTANGLE, 0, timeStart, gaphigh, timeEnd, gaplow); // Set the properties of the rectangle ObjectSetInteger(0, previousGapObjName, OBJPROP_COLOR, clrRed); ObjectSetInteger(0, previousGapObjName, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, previousGapObjName, OBJPROP_WIDTH, 2); ObjectSetInteger(0, previousGapObjName, OBJPROP_RAY, false); // Update the previous gap information previousGapHigh = gaphigh; previousGapLow = gaplow; }
Y así integramos todas las reglas de la estrategia en la función OnTick(), y hemos terminado.
//+------------------------------------------------------------------+ //| OnTick function | //+------------------------------------------------------------------+ void OnTick() { int bars = iBars(_Symbol,PERIOD_CURRENT); if (barsTotal!= bars){ barsTotal = bars; double ma[]; double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); CopyBuffer(handleMa,BASE_LINE,1,1,ma); if (IsBrokenLow()&&sellpos == buypos&&newFVGformed ==1&&bid<ma[0]){ executeSell(); newFVGformed =0; } else if (IsBrokenUp()&&sellpos == buypos&&newFVGformed ==1&&ask>ma[0]){ executeBuy(); newFVGformed =0; } getFVG(); if(buypos>0&&(!PositionSelectByTicket(buypos)|| PositionGetInteger(POSITION_MAGIC) != Magic)){ buypos = 0; } if(sellpos>0&&(!PositionSelectByTicket(sellpos)|| PositionGetInteger(POSITION_MAGIC) != Magic)){ sellpos = 0; } } }
Algunas notas al margen: Llamamos esto al principio de la función OnTick() para que solo procese el resto de las líneas después de que se haya formado una nueva barra. Esta medida ahorra potencia informática.
int bars = iBars(_Symbol,PERIOD_CURRENT); if (barsTotal!= bars){ barsTotal = bars;
Además, dado que solo queremos una operación a la vez, solo podemos entrar en una operación cuando ambos tickets estén configurados en 0 con esta lógica, que comprueba que no haya posiciones abiertas actualmente por este EA en particular.
if(buypos>0&&(!PositionSelectByTicket(buypos)|| PositionGetInteger(POSITION_MAGIC) != Magic)){ buypos = 0; } if(sellpos>0&&(!PositionSelectByTicket(sellpos)|| PositionGetInteger(POSITION_MAGIC) != Magic)){ sellpos = 0; }
Resumen rápido:
- Declaraciones globales y entradas: Configurar el entorno, las variables y los parámetros configurables por el usuario.
- Inicialización (OnInit): Prepara el filtro de media móvil y establece los números mágicos.
- Lógica OnTick: El flujo de trabajo principal: comprueba si hay nuevas barras, detecta FVG, comprueba las rupturas y ejecuta operaciones si se cumplen las condiciones.
- Detección de FVG (getFVG, IsGapValid, IsReacted): Identificar y validar las diferencias de valor razonable y sus reacciones en el mercado.
- Comprobaciones de ruptura (IsBrokenUp, IsBrokenLow): Confirmar la dirección de la ruptura para las entradas comerciales.
- Gestión de operaciones (OnTradeTransaction, executeBuy, executeSell): Gestionar los tickets de órdenes y garantizar que las operaciones se realicen correctamente.
- Gráficos (DrawGap): Visualiza los FVG identificados.
- Filtrado por hora (IsWithinTradingHours): Restringe las operaciones a horas específicas.<segmento 0097 ¶>
Pruebas de la estrategia
Esta estrategia funciona mejor con índices bursátiles debido a sus spreads relativamente bajos y su alta volatilidad, factores beneficiosos para las operaciones intradía de inversores minoristas. Probaremos esta estrategia operando con el índice Nasdaq 100 desde el 1 de enero de 2020 hasta el 1 de diciembre de 2024, en el marco temporal de 3 minutos (M3). Estos son los parámetros que he elegido para esta estrategia.

Aquí tienes algunas recomendaciones para elegir los valores de los parámetros de la estrategia:
- Establezca el horario de negociación durante períodos de alta volatilidad del mercado, normalmente cuando la bolsa está abierta. Este tiempo depende de la hora del servidor de su broker. Por ejemplo, con la hora de mi servidor (GMT+0), el mercado de valores está abierto aproximadamente de 14:00 a 19:00.
- Se recomienda una relación recompensa/riesgo superior a uno porque estamos aprovechando la tendencia macro en un mercado altamente volátil. Además, evite establecer los niveles de toma de ganancias y límite de pérdidas (TPSL) demasiado altos o demasiado bajos. Si el TPSL es demasiado grande, no capturará eficazmente las señales de patrones a corto plazo, y si es demasiado pequeño, los spreads pueden afectar negativamente la operación.
- No ajuste en exceso los valores de los umbrales de gap, los umbrales de velas de ruptura y el período de observación retrospectiva. Mantenga estos parámetros dentro de un rango razonable en relación con el rango de precios del valor negociado para evitar el sobreajuste.
Aquí está el resultado de la prueba retrospectiva:



Podemos observar que la estrategia ha tenido un desempeño muy consistente durante los últimos cinco años, lo que indica su potencial de rentabilidad.
Una operación típica en la parte de visualización del probador de estrategias sería así:

Animo a los lectores a que desarrollen este marco estratégico y a que añadan su creatividad para mejorarlo. Aquí están algunas de mis sugerencias:
- La fuerza del IFVG está determinada por el número de velas de rechazo alrededor del área del FVG. Puedes utilizar la diferencia entre estos números como regla de evaluación.
- En este artículo, nos hemos centrado únicamente en los puntos de ruptura máximos. Sin embargo, en ocasiones la vela de ruptura puede ser demasiado pequeña, lo que indica una fuerza de ruptura débil, lo que podría afectar negativamente la continuación de la tendencia. También puedes considerar agregar un umbral para los puntos de ruptura mínimos.
- La regla de salida se define mediante el take profit y el stop loss. Alternativamente, puede establecer el nivel de salida en función de los niveles clave relevantes para ambas direcciones durante un período de observación determinado, o establecer un tiempo de salida fijo.
Conclusión
En este artículo, presento mi enfoque, desarrollado por mí mismo, para cuantificar y utilizar las brechas de valor razonable inverso como estrategia para los asesores expertos de MetaTrader 5, abarcando la motivación, el desarrollo y las pruebas de la estrategia. Esta estrategia demuestra un alto potencial de rentabilidad, habiendo tenido un desempeño consistente durante los últimos cinco años con más de 400 operaciones. Se pueden realizar modificaciones adicionales para adaptar esta estrategia a diferentes valores y plazos. El código completo se adjunta a continuación y puede integrarlo libremente en sus propios desarrollos comerciales.
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/16659
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Optimización por herencia sanguínea — Blood inheritance optimization (BIO)
Simulación de mercado (Parte 12): Sockets (VI)
Simulación de mercado (Parte 13): Sockets (VII)
Dominando los registros (Parte 4): Guardar registros en archivos
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso