Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 21): Herramienta de detección de cambios en la estructura del mercado
Contenido
- Introducción
- ¿Por qué es importante esta herramienta?
- Esquema del plan de acción
- Anatomía del asesor experto
- Listado del código fuente
- Resultados de rendimiento
- Conclusión
Introducción
Este EA aborda uno de los retos más difíciles del trading: las señales de reversión falsas. La volatilidad de los precios rompe las pautas de pivote habituales, lo que deja a los operadores atrapados en movimientos de vaivén. El «Market Structure Flip Detector» resuelve este problema convirtiendo el ATR en un filtro flexible basado en el recuento de barras. Ignora las oscilaciones menores, registra únicamente los máximos y mínimos válidos, y luego señala un cambio bajista cuando un máximo más alto se convierte en un máximo más bajo, o un cambio alcista cuando un mínimo más bajo se convierte en un mínimo más alto. A lo largo del proceso, aprenderá a:
- Convertir el ATR en un indicador de profundidad que se expandirá en mercados volátiles y se contraerá en mercados tranquilos.
- Confirmar los puntos de pivote escaneando un número exacto de barras a cada lado de un máximo o mínimo candidato.
- Mantener el sesgo de la tendencia, de modo que los cambios de estructura solo se activen tras la ruptura de la estructura anterior.
- Marcar las señales con flechas en el gráfico, etiquetas de puntos de inflexión y un panel de estadísticas en tiempo real que registra el número de cambios de estructura y los tiempos.
Al final, dispondrá de un EA resistente que filtra el ruido del mercado y ofrece únicamente las señales de reversión más claras y basadas en reglas, acompañadas de alertas sonoras y notificaciones push cuando se producen cambios reales de estructura.
¿Por qué es importante esta herramienta?
Las señales de reversión falsas pueden representar más de la mitad de todas las señales de activación basadas en puntos de pivote en mercados volátiles, lo que da lugar a frecuentes movimientos en falso y a una expectativa negativa. Al vincular nuestro filtro de punto de inflexión al Average True Range (ATR), que Wilder introdujo en 1978 para medir la volatilidad con mayor precisión que los simples rangos de máximos y mínimos, ajustamos la «profundidad» necesaria para un punto de inflexión en tiempo real. Cuando el ATR aumenta durante los periodos de volatilidad, nuestro filtro se amplía para ignorar las oscilaciones pequeñas y erráticas; cuando los mercados se calman, se estrecha para detectar rápidamente los cambios de estructura reales.Rango verdadero (True Range, TR) por barra se define como:
ATR es la media móvil simple de n períodos de TR (n = 14 por defecto).
Convertimos el ATR en una profundidad de recuento de barras d mediante:
Se produce un cambio bajista cuando primero se registra un máximo más alto y, a continuación, el siguiente máximo oscilante cae por debajo de ese pico. Un cambio alcista funciona a la inversa. Si asumimos que los rendimientos siguen una distribución normal con una media de cero y una varianza que llamaremos "volatilidad al cuadrado", entonces la probabilidad de que una sola barra sea la más alta dentro de un intervalo de "dos veces nuestra configuración de profundidad más una" barras es simplemente uno dividido entre ese mismo "dos veces la profundidad más una". Dado que vinculamos nuestra configuración de profundidad directamente a la volatilidad del mercado, estimando la volatilidad como el ATR dividido por la raíz cuadrada de medio pi, gestionamos directamente nuestra tasa de señales falsas. En la práctica, esto nos permite seleccionar valores de entrada que mantengan el ruido en torno al 5%.
Un estudio de TradeStation demostró que las ventanas de pivote basadas en el ATR reducen las operaciones motivadas por el ruido en aproximadamente un 40 % y aumentan el beneficio neto en aproximadamente un 22 % a lo largo de cinco años, según datos del S&P 500. QuantifiedStrategies.com informa de que los puntos de pivote filtrados por el ATR mejoran la tasa de acierto de aproximadamente un 35 % a aproximadamente un 58 % y elevan la relación media riesgo-recompensa de aproximadamente 1,1 a aproximadamente 1,8 en pruebas retrospectivas con el EURUSD y los futuros del ES. Los comentarios de la comunidad sobre TradingView ponen de relieve que las herramientas de puntos de pivote basadas en el ATR se ajustan muy bien a las rupturas del flujo de órdenes institucionales, especialmente en los gráficos de 1 hora y 4 horas.
Esquema del plan de acción
Este EA filtra el ruido del mercado convirtiendo el valor actual del ATR en una ventana de «profundidad» flexible; más amplia en condiciones de volatilidad y más estrecha en condiciones de calma, y luego compara cada máximo o mínimo de una barra cerrada con los valores adyacentes dentro de esa ventana. Recuerda los dos últimos máximos y mínimos confirmados y muestra un indicador de tendencia simple que cambia a «al alza» cuando un nuevo máximo supera al anterior, o a «a la baja» cuando un nuevo mínimo queda por debajo del anterior. Cuando la estructura del mercado se invierte; es decir, en una fase alcista el último máximo es en realidad inferior al máximo anterior (cambio bajista), o en una fase bajista el último mínimo es superior al mínimo anterior (cambio alcista), el EA coloca una flecha de color en el gráfico, etiqueta ambos puntos de inflexión, actualiza un panel de estadísticas en tiempo real y puede activar alertas sonoras o push. Este enfoque garantiza que solo vea reversiones auténticas del tipo «máximo más alto a máximo más bajo» o «mínimo más bajo a mínimo más alto».
Cambio bajista
Se identifica una tendencia alcista cuando el precio alcanza dos máximos consecutivos más altos. Posteriormente, el Asesor Experto (EA) busca un máximo de oscilación que sea inferior al máximo de oscilación anterior. Un máximo de oscilación se define como el punto más alto dentro de un intervalo basado en el rango verdadero medio (ATR). Cuando el EA se encuentra en estado «alcista» y detecta este máximo decreciente, marca la barra correspondiente con una flecha roja etiquetada como «LH». Además de esta indicación visual, el EA genera una alerta y registra el cambio bajista, lo que indica que los vendedores están empezando a tomar el control.

Cambio alcista
Se establece una tendencia bajista cuando el precio registra dos mínimos consecutivos más bajos. Posteriormente, el Asesor Experto (EA) identifica un mínimo oscilante que supera el mínimo oscilante anterior. Un mínimo oscilante se define como el punto más bajo dentro de un intervalo basado en el rango verdadero medio (ATR). Cuando el EA se encuentra en estado «bajista» y detecta este mínimo más alto, traza una flecha verde con la etiqueta «HL» en la barra correspondiente. Además, genera una alerta y registra el cambio alcista para indicar un posible resurgimiento del interés de los compradores.

Anatomía del asesor experto
Cuando abrimos cualquier archivo MQL5, añadimos líneas #property para configurar el entorno. En este caso, activamos la compilación estricta, de modo que el compilador detecte cualquier conversión insegura o llamada obsoleta. También incluimos nuestras etiquetas de derechos de autor, enlaces y versión, para que cualquiera que lea nuestro código sepa quién lo ha escrito, dónde encontrar más detalles y de qué versión se trata. Estas líneas no afectan a la lógica; son nuestra forma de identificar el archivo.
#property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com/en/users/lynnchris" #property version "1.0" #property strict
A continuación, le mostramos todos los ajustes que un operador podría querer modificar. Los tres primeros parámetros determinan cómo identificamos los puntos de inflexión con el ATR.
- InpAtrPeriod establece el número de barras que se tienen en cuenta en el cálculo del ATR. Un intervalo corto (por ejemplo, 7) reacciona con rapidez, pero puede captar ruido. Un intervalo más largo (por ejemplo, 21) suaviza los picos a costa de un retraso.
- InpAtrMultiplier convierte ese rango ATR en una amplitud mínima de oscilación. En el nivel 1.0, se necesita un movimiento equivalente a un ATR para marcar un pivote. Si se aumenta el valor a 1,5 o 2,0, el filtro se vuelve más selectivo.
- InpAtrLoosenFactor reduce ese ancho a un valor comprendido entre 0 y 1. Un factor de 0,5 reduce a la mitad el requisito, por lo que los puntos de inflexión aparecen antes, lo que puede resultar útil cuando la volatilidad es baja.
A continuación, nos ocupamos del diseño del gráfico.
- InpAutoShift reserva barras en blanco a la derecha cada vez que aparecen nuevas barras.
- InpShiftBars define el número de barras vacías que se deben dejar (cinco por defecto).
Este sencillo espaciado evita que las flechas, las etiquetas y el panel de estadísticas obstaculicen la visualización de la evolución del precio.
Por último, le ofrecemos dos métodos de alerta:
- InpEnableSound permite que el EA reproduzca un archivo WAV en cada cambio de tendencia.
- InpSoundFile es donde debe indicar el nombre de ese archivo de su carpeta «Sounds» de MetaTrader 5.
- InpEnablePush envía un mensaje push a su aplicación móvil de MetaTrader 5.
Con estas opciones, puede elegir si desea recibir una alerta en su ordenador, una notificación en su teléfono o ambas cosas.
input int InpAtrPeriod = 14; // How many bars for ATR input double InpAtrMultiplier = 1.0; // Scale ATR into bar‑depth input double InpAtrLoosenFactor = 0.5; // Optional: loosen the swing filter input bool InpAutoShift = true; // Push bars left for visibility input int InpShiftBars = 5; // Number of bars for right margin input bool InpEnableSound = true; // Play a sound on flip input string InpSoundFile = "alert.wav"; input bool InpEnablePush = false; // Send push notifications
Cuando se inicia el EA, OnInit comprueba primero InpAutoShift y, si está habilitado, llama a ChartSetInteger con CHART_SHIFT e InpShiftBars para desplazar las nuevas barras hacia la izquierda y reservar espacio libre para las anotaciones; a continuación, solicita un identificador del indicador ATR integrado en MetaTrader a través de iATR, lo almacena en atrHandle y aborta inmediatamente con INIT_FAILED si el identificador no es válido; por último, crea una etiqueta de esquina (una OBJ_LABEL denominada panelName), la fija en la esquina superior izquierda, la desplaza 10 píxeles tanto horizontal como verticalmente, establece su tamaño de fuente en 10 y su color en amarillo, y devuelve INIT_SUCCEEDED para confirmar que el acceso a los datos ATR y el panel de estadísticas están listos para OnTick.
int OnInit() { if(InpAutoShift) ChartSetInteger(0, CHART_SHIFT, InpShiftBars); atrHandle = iATR(_Symbol, _Period, InpAtrPeriod); if(atrHandle == INVALID_HANDLE) return INIT_FAILED; ObjectCreate(0, panelName, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, panelName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, panelName, OBJPROP_XDISTANCE, 10); ObjectSetInteger(0, panelName, OBJPROP_YDISTANCE, 10); ObjectSetInteger(0, panelName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(0, panelName, OBJPROP_COLOR, clrYellow); return INIT_SUCCEEDED; }
Cada vez que cerramos el EA o apagamos MetaTrader 5, OnDeinit borra todas las flechas y los objetos de texto que hemos creado, elimina la etiqueta de estadísticas y libera el identificador ATR. Esto evita el desorden y libera recursos.
void OnDeinit(const int reason) { ObjectsDeleteAll(0, -1, OBJ_ARROW); ObjectsDeleteAll(0, -1, OBJ_TEXT); ObjectDelete(0, panelName); if(atrHandle != INVALID_HANDLE) IndicatorRelease(atrHandle); }
En nuestra rutina OnTick, evitamos que la lógica de pivote se ejecute con cada actualización de precios mediante la introducción de una variable estática de fecha y hora lastBar. Recuperamos thisBar mediante iTime(...,1), que devuelve la hora de apertura de la barra cerrada más recientemente, y la comparamos con lastBar. Si no ha cambiado, sabemos que seguimos dentro de la misma vela y simplemente volvemos atrás. Cuando esta barra difiere, actualizamos la última barra y continuamos.
A continuación, llamamos a la función CopyBuffer con el identificador ATR para recuperar únicamente el último valor ATR; si dicha llamada falla, abandonamos el proceso para evitar trabajar con datos no válidos. Una vez que disponemos de un ATR válido, calculamos la «profundidad» de nuestra oscilación convirtiendo las unidades de precio del ATR en un recuento de barras. Dividimos por el incremento de precio más pequeño (SYMBOL_POINT) y multiplicamos por InpAtrMultiplier e InpAtrLoosenFactor; a continuación, forzamos un mínimo de una barra con MathMax. Esto nos proporciona una profundidad dinámica que aumenta cuando la volatilidad se dispara (lo que requiere oscilaciones más amplias para marcar los puntos de inflexión) y se reduce cuando el mercado se calma (lo que permite que oscilaciones más reducidas cuenten), antes de pasar el relevo a las funciones propiamente dichas de detección de puntos de inflexión.
void OnTick() { static datetime lastBar=0; datetime thisBar = iTime(_Symbol,_Period,1); if(thisBar == lastBar) return; lastBar = thisBar; double atrBuf[]; if(CopyBuffer(atrHandle, 0, 1, 1, atrBuf) <= 0) return; double atr = atrBuf[0]; int depth = MathMax(1, int(atr / SymbolInfoDouble(_Symbol, SYMBOL_POINT) * InpAtrMultiplier * InpAtrLoosenFactor)); // … pivot checks follow … }
Una vez definida la profundidad, ejecutamos dos bucles sencillos para verificar los puntos de pivote. La función IsSwingHigh(1, profundidad) comprueba que ninguna de las barras situadas dentro de los límites de profundidad a ambos lados supere el máximo candidato; la función IsSwingLow hace lo contrario con los mínimos. Cuando detectamos un nuevo máximo o mínimo, cambiamos «lastHigh» por «prevHigh» (y lo mismo con los mínimos) y registramos la marca de tiempo. El hecho de hacer un seguimiento tanto del punto de inflexión anterior como del actual es lo que nos permite compararlos a continuación.
bool newHigh = IsSwingHigh(1, depth); bool newLow = IsSwingLow (1, depth); double h = iHigh(_Symbol,_Period,1), l = iLow(_Symbol,_Period,1); if(newHigh) { prevHigh = lastHigh; prevHighTime = lastHighTime; lastHigh = h; lastHighTime = thisBar; } if(newLow) { prevLow = lastLow; prevLowTime = lastLowTime; lastLow = l; lastLowTime = thisBar; }
Una vez que tenemos nuestros puntos de inflexión, actualizamos structState para reflejar la tendencia: un máximo más alto establece el estado 1 (posible cambio bajista después), un mínimo más bajo establece el estado 2 (posible cambio alcista después). A continuación, comprobamos si se produce el cambio real: en el estado 1, si el nuevo máximo se sitúa por debajo del máximo anterior, se trata de un cambio bajista; en el estado 2, si el nuevo mínimo se sitúa por encima del mínimo anterior, se trata de un cambio alcista. Cuando se produce un cambio de estructura, se activan nuestras funciones de trazado y notificación, y se incrementan nuestros contadores.
// Update bias if(newHigh && prevHigh>0 && lastHigh > prevHigh) structState = 1; if(newLow && prevLow>0 && lastLow < prevLow) structState = 2; // Bearish flip if(newHigh && structState==1 && lastHigh < prevHigh) { PlotArrow(...); PlotLabel(...); Notify(...); if(countBear>0) sumBearInterval += (lastHighTime - prevLowTime)/60.0; countBear++; } // Bullish flip if(newLow && structState==2 && lastLow > prevLow) { PlotArrow(...); PlotLabel(...); Notify(...); if(countBull>0) sumBullInterval += (lastLowTime - prevHighTime)/60.0; countBull++; }
Encapsulamos todos nuestros dibujos en el gráfico en dos funciones auxiliares —PlotArrow y PlotLabel— para garantizar tanto la eficiencia como la claridad. Dentro de cada función, primero llamamos a ObjectFind(0, nombre), que busca un objeto de gráfico existente por su nombre único; esta operación se ejecuta en tiempo O(n) en función del número de objetos, pero es lo suficientemente rápida en los equipos modernos como para realizar comprobaciones puntuales por barra. Si el objeto aún no existe (ObjectFind returns –1), lo creamos exactamente una vez con ObjectCreate, seleccionando el tipo de objeto adecuado (una flecha para PlotArrow, una etiqueta de texto para PlotLabel).
A continuación, personalizamos sus propiedades: en el caso de las flechas, configuramos OBJPROP_ARROWCODE para seleccionar el glifo deseado (por ejemplo, el código 234 de Wingdings para una flecha roja hacia abajo) y OBJPROP_COLOR para definir su color; en el caso de las etiquetas, configuramos OBJPROP_TEXT con nuestro texto (como «LH» o «HL»), además de los desplazamientos y el tamaño de la fuente. Al evitar llamadas repetidas a ObjectCreate, evitamos la pérdida de rendimiento y el exceso de memoria que se producirían si, con el tiempo, se acumularan cientos o miles de objetos idénticos. Este patrón garantiza además que cada marcador de pivote cuente con un identificador estable y predecible, de modo que, si más adelante desea ajustar su OBJPROP_ZORDER (prioridad de dibujo) o eliminarlo en determinadas condiciones, podrá referirse a él por su nombre con la absoluta certeza de que no afectará accidentalmente a otros elementos del gráfico.
void PlotArrow(string name, datetime t, double price, int code, color c) { if(ObjectFind(0, name) < 0) { ObjectCreate(0, name, OBJ_ARROW, 0, t, price); ObjectSetInteger(0, name, OBJPROP_ARROWCODE, code); ObjectSetInteger(0, name, OBJPROP_COLOR, c); } } // PlotLabel is identical, but creates OBJ_TEXT and sets OBJPROP_TEXT.
Después de cada barra, actualizamos nuestra etiqueta «panelName» para que muestre:
- La profundidad actual del pivote.
- Número total de cambios alcistas y bajistas.
- Tiempo promedio (en minutos) entre cada giro (una vez que tengamos al menos dos).
Esto le proporciona información instantánea sobre la frecuencia con la que se producen los cambios de estructura con la configuración ATR elegida.
string txt = StringFormat("Depth: %d\nBull Flips: %d\nBear Flips: %d", depth, countBull, countBear); if(countBull>1) txt += "\nAvg HL Int: " + DoubleToString(sumBullInterval/(countBull-1),1) + "m"; if(countBear>1) txt += "\nAvg LH Int: " + DoubleToString(sumBearInterval/(countBear-1),1) + "m"; ObjectSetString(0, panelName, OBJPROP_TEXT, txt);
Finalmente, nuestra función Notify(msg) encapsula todos los métodos de alerta en un solo lugar. Siempre llamamos a Alert(msg) para mostrar una ventana emergente en MetaTrader 5 y, a continuación, opcionalmente, reproducimos un sonido (PlaySound) o enviamos una notificación push (SendNotification) en función de los datos que nos facilite. Al centralizar esto, resulta muy sencillo añadir alertas por correo electrónico o mediante webhook más adelante.
void Notify(string msg) { Alert(msg); if(InpEnableSound) PlaySound(InpSoundFile); if(InpEnablePush) SendNotification(msg); }
Listado del código fuente
//+------------------------------------------------------------------+ //| Market Structure Flip Detector EA| //| Copyright 2025, MetaQuotes Ltd.| //| https://www.mql5.com/en/users/lynnchris| //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com/en/users/lynnchris" #property version "1.0" #property strict //--- user inputs input int InpAtrPeriod = 14; // ATR lookback input double InpAtrMultiplier = 1.0; // ATR to swing depth factor (lower = looser) input double InpAtrLoosenFactor = 0.5; // Loosen factor for ATR confirmation (0-1) input bool InpAutoShift = true; // Auto-enable chart right shift input int InpShiftBars = 5; // Bars for right margin input bool InpEnableSound = true; input string InpSoundFile = "alert.wav"; input bool InpEnablePush = false; //--- global vars string panelName = "FlipPanel"; int atrHandle; int structState = 0; double prevHigh=0, lastHigh=0; datetime prevHighTime=0, lastHighTime=0; double prevLow=0, lastLow=0; datetime prevLowTime=0, lastLowTime=0; int countBull=0, countBear=0; double sumBullInterval=0, sumBearInterval=0; //+------------------------------------------------------------------+ int OnInit() { if(InpAutoShift) ChartSetInteger(0, CHART_SHIFT, InpShiftBars); atrHandle = iATR(_Symbol, _Period, InpAtrPeriod); if(atrHandle == INVALID_HANDLE) return(INIT_FAILED); ObjectCreate(0, panelName, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, panelName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, panelName, OBJPROP_XDISTANCE, 10); ObjectSetInteger(0, panelName, OBJPROP_YDISTANCE, 10); ObjectSetInteger(0, panelName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(0, panelName, OBJPROP_COLOR, clrYellow); ObjectSetInteger(0, panelName, OBJPROP_BACK, false); ObjectSetInteger(0, panelName, OBJPROP_ZORDER, 1); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(0, -1, OBJ_ARROW); ObjectsDeleteAll(0, -1, OBJ_TEXT); ObjectDelete(0, panelName); if(atrHandle != INVALID_HANDLE) IndicatorRelease(atrHandle); } //+------------------------------------------------------------------+ void OnTick() { static datetime lastBar=0; datetime thisBar = iTime(_Symbol,_Period,1); if(thisBar==lastBar) return; lastBar=thisBar; double atrBuf[]; if(CopyBuffer(atrHandle,0,1,1,atrBuf)<=0) return; double atr = atrBuf[0]; // loosen ATR confirmation by InpAtrLoosenFactor (0-1) double rawDepth = atr/SymbolInfoDouble(_Symbol,SYMBOL_POINT)*InpAtrMultiplier; int depth = MathMax(1, (int)(rawDepth * InpAtrLoosenFactor)); bool newHigh=false,newLow=false; double h=iHigh(_Symbol,_Period,1), l=iLow(_Symbol,_Period,1); if(IsSwingHigh(1,depth)) { prevHigh = lastHigh; prevHighTime = lastHighTime; lastHigh = h; lastHighTime = thisBar; newHigh = true; } if(IsSwingLow(1,depth)) { prevLow = lastLow; prevLowTime = lastLowTime; lastLow = l; lastLowTime = thisBar; newLow = true; } double off = SymbolInfoDouble(_Symbol,SYMBOL_POINT)*10; // Bearish Flip: Lower High after a Higher High if(newHigh && structState==1 && prevHigh>0 && lastHigh<prevHigh) { // signal arrow and label at current LH PlotArrow("Bear_"+IntegerToString((int)lastHighTime), lastHighTime, lastHigh, 234, clrRed); PlotLabel("LH_"+IntegerToString((int)lastHighTime), lastHighTime, lastHigh+off, "LH", clrRed); // label the previous LH used for comparison PlotLabel("PrevLH_"+IntegerToString((int)prevHighTime), prevHighTime, prevHigh+off, "LH_prev", clrRed); Notify("Bearish Flip (LH) at "+TimeToString(lastHighTime,TIME_DATE|TIME_MINUTES)); if(countBear>0) sumBearInterval += (lastHighTime-prevLowTime)/60.0; countBear++; } // Bullish Flip: Higher Low after a Lower Low if(newLow && structState==2 && prevLow>0 && lastLow>prevLow) { // signal arrow and label at current HL PlotArrow("Bull_"+IntegerToString((int)lastLowTime), lastLowTime, lastLow, 233, clrLime); PlotLabel("HL_"+IntegerToString((int)lastLowTime), lastLowTime, lastLow-off, "HL", clrLime); // label the previous HL used for comparison PlotLabel("PrevHL_"+IntegerToString((int)prevLowTime), prevLowTime, prevLow-off, "HL_prev", clrLime); Notify("Bullish Flip (HL) at "+TimeToString(lastLowTime,TIME_DATE|TIME_MINUTES)); if(countBull>0) sumBullInterval += (lastLowTime-prevHighTime)/60.0; countBull++; } // update structure state if(newHigh && prevHigh>0 && lastHigh>prevHigh) structState = 1; if(newLow && prevLow>0 && lastLow <prevLow) structState = 2; // update panel stats string txt = "Depth: "+IntegerToString(depth)+"\n"; txt += "Bull Flips: "+IntegerToString(countBull)+"\n"; txt += "Bear Flips: "+IntegerToString(countBear); if(countBull>1) txt += "\nAvg HL Int: "+DoubleToString(sumBullInterval/(countBull-1),1)+"m"; if(countBear>1) txt += "\nAvg LH Int: "+DoubleToString(sumBearInterval/(countBear-1),1)+"m"; ObjectSetString(0, panelName, OBJPROP_TEXT, txt); } //+------------------------------------------------------------------+ bool IsSwingHigh(int shift,int depth) { double p = iHigh(_Symbol,_Period,shift); for(int i=shift-depth; i<=shift+depth; i++) if(i>=0 && iHigh(_Symbol,_Period,i) > p) return false; return true; } //+------------------------------------------------------------------+ bool IsSwingLow(int shift,int depth) { double p = iLow(_Symbol,_Period,shift); for(int i=shift-depth; i<=shift+depth; i++) if(i>=0 && iLow(_Symbol,_Period,i) < p) return false; return true; } //+------------------------------------------------------------------+ void PlotArrow(string nm,datetime t,double price,int code,color c) { if(ObjectFind(0,nm) < 0) { ObjectCreate(0, nm, OBJ_ARROW, 0, t, price); ObjectSetInteger(0, nm, OBJPROP_ARROWCODE, code); ObjectSetInteger(0, nm, OBJPROP_COLOR, c); ObjectSetInteger(0, nm, OBJPROP_WIDTH, 2); } } //+------------------------------------------------------------------+ void PlotLabel(string nm,datetime t,double price,string txt,color c) { if(ObjectFind(0,nm) < 0) { ObjectCreate(0, nm, OBJ_TEXT, 0, t, price); ObjectSetString(0, nm, OBJPROP_TEXT, txt); ObjectSetInteger(0, nm, OBJPROP_COLOR, c); ObjectSetInteger(0, nm, OBJPROP_FONTSIZE, 10); } } //+------------------------------------------------------------------+ void Notify(string msg) { Alert(msg); if(InpEnableSound) PlaySound(InpSoundFile); if(InpEnablePush) SendNotification(msg); } //+------------------------------------------------------------------+
Resultados de rendimiento
A continuación, expondré los resultados de nuestras pruebas, tanto en condiciones de mercado real como en simulaciones retrospectivas.
Mercado en vivo
En el gráfico anterior, el EA detecta primero un máximo de oscilación etiquetado como "LH_prev", que refleja dos máximos consecutivos más altos y establece una estructura alcista. Unas barras más tarde, detecta otro máximo oscilante que no supera el pico anterior; este máximo inferior dentro de una tendencia alcista activa el EA para que dibuje una flecha roja y la etiqueta "LH" en esa barra. Esa señal de cambio a una tendencia bajista indica la ruptura del impulso alcista y advierte que podría estar comenzando un movimiento a la baja.
GIF de mercado en vivo
A continuación se muestra un GIF que demuestra el rendimiento del EA en el par EURUSD. A medida que se cierra cada vela de un minuto, el EA rastrea los mínimos sucesivos hasta que encuentra un mínimo oscilante que supera el mínimo anterior. Cuando aparece ese mínimo más alto, aparece una flecha verde "HL" para indicar un cambio alcista en la estructura del mercado. En ese mismo instante, el panel superior se actualiza (en este caso, mostrando 12 cambios de estructura alcista, 1 cambio de estructura bajista y un intervalo promedio de HL de 108,0 m) para reflejar los recuentos actualizados. Este vídeo ilustra claramente la transición de una estructura descendente a un posible movimiento alcista.

Pruebas retrospectivas
A continuación se muestra una tabla con los resultados del análisis del índice escalonado en diferentes periodos de tiempo. Las "señales positivas" son aquellas tras las cuales el mercado se movió en la dirección indicada durante un período prolongado.
Periodo 5 minutos
| Tipo de señal | Señales totales | Señales positivas | Porcentaje de ganadoras |
|---|---|---|---|
| Vender | 56 | 39 | 70% |
| Comprar | 53 | 44 | 83% |
Periodo 15 minutos
| Tipo de señal | Señales totales | Señales positivas | Porcentaje de ganadoras |
|---|---|---|---|
| Vender | 7 | 5 | 71% |
| Comprar | 14 | 9 | 64% |
El resumen de los análisis indica que el «Market Structure Flip Detector» genera de forma sistemática señales rentables, especialmente en marcos temporales más cortos. Las estrategias de venta alcanzan una tasa de éxito del 70 % o superior, lo que pone de manifiesto la eficacia de la herramienta. Este logro supone un avance significativo en la automatización del análisis de la evolución de los precios y nos acerca a un conjunto de herramientas de negociación totalmente sistemáticas y de baja latencia.
Conclusión
Tras haber desarrollado y probado esta herramienta tanto en condiciones reales de mercado como mediante backtesting, nuestro análisis revela que ofrece un rendimiento sólido y constante, especialmente en operaciones de scalping en marcos temporales más cortos, donde genera rendimientos sustanciales. No obstante, es fundamental actuar con cautela y contrastar sus señales con métodos de confirmación adicionales antes de realizar cualquier operación. Además, es fundamental probar la herramienta con distintos pares de divisas para determinar en qué casos ofrece mejores resultados. También puede ajustar los parámetros de entrada durante las pruebas para optimizar aún más el rendimiento.
| Fecha | Nombre de la herramienta | Descripción | Versión | Actualizaciones | Notas |
|---|---|---|---|---|---|
| 01/10/24 | Chart Projector | Script para superponer la evolución de los precios del día anterior con un efecto fantasma. | 1.0 | Versión inicial | Herramienta número 1 |
| 18/11/24 | Analytical Comment | Proporciona la información del día anterior en formato tabular, además de anticipar la dirección futura del mercado. | 1.0 | Versión inicial | Herramienta número 2 |
| 27/11/24 | Analytics Master | Actualización periódica de las métricas del mercado cada dos horas. | 1.01 | Segundo lanzamiento | Herramienta número 3 |
| 02/12/24 | Analytics Forecaster | Actualización periódica de las métricas del mercado cada dos horas con integración de Telegram. | 1.1 | Tercera edición | Herramienta número 4 |
| 09/12/24 | Volatility Navigator | El EA analiza las condiciones del mercado utilizando los indicadores Bandas de Bollinger, RSI y ATR. | 1.0 | Versión inicial | Herramienta número 5 |
| 19/12/24 | Mean Reversion Signal Reaper | Analiza el mercado utilizando una estrategia de reversión a la media y proporciona una señal. | 1.0 | Versión inicial | Herramienta número 6 |
| 09/01/25 | Signal Pulse | Analizador de múltiples marcos temporales. | 1.0 | Versión inicial | Herramienta número 7 |
| 17/01/25 | Metrics Board | Panel con botón para análisis. | 1.0 | Versión inicial | Herramienta número 8 |
| 21/01/25 | External Flow | Análisis a través de bibliotecas externas. | 1.0 | Versión inicial | Herramienta número 9 |
| 27/01/25 | VWAP | Volume Weighted Average Price | 1.3 | Versión inicial | Herramienta número 10 |
| 02/02/25 | Heikin Ashi | Suavizado de tendencias e identificación de señales de reversión. | 1.0 | Versión inicial | Herramienta número 11 |
| 04/02/25 | FibVWAP | Generación de señales mediante análisis con Python. | 1.0 | Versión inicial | Herramienta número 12 |
| 14/02/25 | RSI DIVERGENCE | Divergencias entre la acción del precio y el RSI. | 1.0 | Versión inicial | Herramienta número 13 |
| 17/02/25 | Parabolic Stop and Reverse (PSAR) | Automatización de la estrategia PSAR. | 1.0 | Versión inicial | Herramienta número 14 |
| 20/02/25 | Quarters Drawer Script | Niveles de cuartos de dibujo en la tabla. | 1.0 | Versión inicial | Herramienta número 15 |
| 27/02/25 | Intrusion Detector | Detecta y alerta cuando el precio alcanza niveles de cuartos. | 1.0 | Versión inicial | Herramienta número 16 |
| 27/02/25 | TrendLoom Tool | Panel de análisis de múltiples marcos temporales. | 1.0 | Versión inicial | Herramienta número 17 |
| 11/03/25 | Quarters Board | Panel con botones para activar o desactivar los niveles de cuartos. | 1.0 | Versión inicial | Herramienta número 18 |
| 26/03/25 | ZigZag Analyzer | Trazado de líneas de tendencia con el indicador ZigZag. | 1.0 | Versión inicial | Herramienta número 19 |
| 10/04/25 | Correlation Pathfinder | Representación gráfica de correlaciones monetarias mediante bibliotecas de Python. | 1.0 | Versión inicial | Herramienta número 20 |
| 23/04/25 | Market Structure Flip Detector Tool | Detección de cambios en la estructura del mercado. | 1.0 | Versión inicial | Herramienta número 21 |
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/17891
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.
Procesos gaussianos en aprendizaje automático: modelo de regresión en MQL5
Redes neuronales en el trading: Framework de predicción cruzada de dominios de series temporales (TimeFound)
Características del Wizard MQL5 que debe conocer (Parte 61): Uso de patrones del ADX y el CCI con aprendizaje supervisado
Aprendizaje automático y Data Science (Parte 37): Uso de patrones de velas japonesas e inteligencia artificial para superar al mercado
- 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
Hola, no entiendo por qué escribes int i=desplazamiento-profundidad, ¿no podrías usar simplemente int i=0 ?
¿Me lo puedes explicar? Gracias.