English Русский 中文 Deutsch 日本語
preview
Características del Wizard MQL5 que debe conocer (Parte 61): Uso de patrones del ADX y el CCI con aprendizaje supervisado

Características del Wizard MQL5 que debe conocer (Parte 61): Uso de patrones del ADX y el CCI con aprendizaje supervisado

MetaTrader 5Sistemas comerciales |
39 0
Stephen Njuki
Stephen Njuki

Introducción

Continuamos analizando cómo se pueden combinar los indicadores que rastrean diferentes aspectos de los mercados con el aprendizaje automático para construir un sistema de negociación. En los próximos artículos, analizaremos la combinación del oscilador Average Directional Index (ADX) con el Commodity Channel Index (CCI). El ADX es principalmente un indicador de confirmación de tendencias, mientras que el CCI es un indicador de impulso. Ya mencionamos estas dos características cuando analizamos las tendencias de los indicadores individuales en artículos anteriores, como este. En resumen, la confirmación de la tendencia mide la fuerza de una determinada tendencia de precios; una mayor fuerza indica que es un buen momento para entrar en ella. Por otro lado, los indicadores de impulso miden la tasa de cambio de precios. Cuanto más rápidamente cambie el precio en una dirección determinada, menos probable será sufrir fluctuaciones adversas.

Logo



Commodity Channel Index (CCI)

Estamos probando nuestras redes, que utilizan señales de indicadores como entrada, en Python. Esto se debe a que Python ofrece actualmente una ventaja de rendimiento significativa en comparación con MQL5. Sin embargo, como he mencionado en artículos recientes, MQL5 puede igualar o acercarse a estos resultados utilizando OpenCL. Sin embargo, para beneficiarse de la velocidad de OpenCL, es necesario disponer de una GPU. Por ahora, sin embargo, cuando ambos lenguajes se utilizan en procesadores similares, Python claramente se impone, por lo que será el lenguaje con el que probaremos y desarrollaremos nuestros modelos.

MetaTrader ha creado un módulo de Python que no solo permite cargar datos de precios de brókeres en Python, sino que también permite realizar operaciones desde Python. Puede encontrar más información al respecto aquí y aquí. Por lo tanto, utilizamos parte de estos módulos para iniciar sesión en nuestro bróker y extraer datos de precios a Python para nuestra red neuronal multicapas (MLP) de aprendizaje supervisado. Una vez que tengamos estos datos de precios, necesitaremos definir las funciones de nuestros indicadores. Si comenzamos con el CCI, nuestra implementación puede ser la siguiente:

def CCI(df, period=14):
    """
    Calculate Commodity Channel Index (CCI)
    :param df: pandas DataFrame with columns ['high', 'low', 'close']
    :param period: lookback period (default 20)
    :return: Series with CCI values
    """
    # Calculate Typical Price
    typical_price = (df['high'] + df['low'] + df['close']) / 3
    
    # Calculate Simple Moving Average of Typical Price
    sma = typical_price.rolling(window=period).mean()
    
    # Calculate Mean Deviation
    mean_deviation = typical_price.rolling(window=period).apply(
        lambda x: np.mean(np.abs(x - np.mean(x))), raw=True)
    
    # Calculate CCI
    df['cci'] = (typical_price - sma) / (0.015 * mean_deviation)
    
    return df[['cci']]

El CCI es un oscilador que mide en qué medida el precio de un activo se desvía de su media estadística. A primera vista, esto puede ser útil para los operadores a la hora de identificar condiciones de sobrecompra o sobreventa. La función que hemos implementado anteriormente toma un dataframe de pandas con columnas de precios 'high', 'low' y 'close', y un período de retrospectiva (con un valor predeterminado de 14) para calcular los valores del CCI. El cálculo del «Typical Price» establece la media de los precios máximo, mínimo y de cierre para representar o abarcar la mayor parte de la evolución del precio durante el período considerado. Se trata de la media aritmética de estos tres precios principales, para cada período de tiempo, y ayuda a simplificar los datos de precios en un único valor representativo. Esto ayuda a reducir el ruido provocado por las fluctuaciones intradía. Este paso es fundamental, ya que el CCI se basa en las desviaciones de este precio típico y el uso de los tres (máximo, mínimo y cierre) ayuda a garantizar una visión equilibrada de la evolución del precio. Por lo tanto, es necesario asegurarse de que el marco de datos de Pandas que recupera los datos de precios de los brókeres a través del módulo MetaTrader 5 tenga todas estas columnas, ya que los valores faltantes provocarán errores.

Una media móvil simple (SMA) suaviza el precio típico durante el período especificado para establecer una línea base. Esto se consigue calculando la SMA durante el período de entrada especificado (por defecto 14) para que pueda actuar como referencia. Esto es importante porque las fluctuaciones de precios a corto plazo se suavizan, proporcionando así un nivel de precios "normal" representativo. La función rolling requiere suficientes puntos de datos, al menos el período de entrada al cuadrado, para poder calcular una SMA válida. Si el conjunto de datos es demasiado corto, los valores NaN iniciales pueden requerir un tratamiento especial (mediante funciones como dropna(), etc.).

La desviación media mide entonces la desviación absoluta promedio de estos precios típicos representativos con respecto a su media móvil simple (SMA), capturando así la volatilidad de los precios. Para cada período, se calcula la diferencia absoluta entre cada precio típico y la media del período, y luego se calcula el promedio. Esto es fundamental porque la desviación media cuantifica la volatilidad de los precios, lo cual es indispensable para escalar el CCI, que a su vez es vital para reflejar las fluctuaciones típicas del precio de un activo. Esto también garantiza la posibilidad de comparar diferentes activos. La aplicación de la función lambda requiere una gran capacidad de cálculo para conjuntos de datos grandes, por lo que sería una buena idea utilizar alternativas vectorizadas o bibliotecas como ta-lib. Para ello, aún es necesario importar NumPy.

La fórmula CCI, por lo tanto, combina todos los componentes anteriores para calcular un valor, que luego se escala mediante una constante de 0,015 para su normalización. La constante de 0,015 es un factor de escala estándar que busca mantener los valores de CCI en el rango de ±100. Sin embargo, esto no siempre se logra, aunque ese es el objetivo. Este es un elemento fundamental de la fórmula del CCI, ya que traduce las desviaciones de precios brutas en un oscilador estandarizado. Los valores superiores a +100 indicarían condiciones de sobrecompra, mientras que los valores inferiores a -100 sugerirían condiciones de sobreventa. Con esta fórmula, conviene tener cuidado con los errores de división por cero si la desviación media es cero. Aunque es poco frecuente, este escenario es posible con precios planos. A continuación, se puede añadir un pequeño valor épsilon (por ejemplo, 1e-10) al denominador según sea necesario para mitigar este efecto. 

La instrucción return devuelve un marco de datos que contiene únicamente la columna 'cci'. Si se requieren columnas adicionales para la depuración o el análisis (por ejemplo, 'typical_price'), esta instrucción puede modificarse para incluirlas.



Average Directional Index (ADX)

Como se mencionó anteriormente, el ADX mide la fuerza de la tendencia, independientemente de la dirección. Esto se logra mediante el uso del "Directional Movement Index", que se compone principalmente de 2 búferes (+DI y -DI). Esta función, al igual que el CCI anterior, toma un marco de datos de pandas con las columnas «high», «low» y «close», así como un periodo de retrospectiva (por defecto, 14), para calcular los valores de ADX, +DI y -DI. Su implementación en Python es la siguiente:

def ADX(df, period=14):
    """
    Calculate Average Directional Index (ADX)
    :param df: pandas DataFrame with columns ['high', 'low', 'close']
    :param period: lookback period (default 14)
    :return: DataFrame with ADX, +DI, -DI columns
    """
    # Calculate +DM, -DM, and True Range
    df['up_move'] = df['high'] - df['high'].shift(1)
    df['down_move'] = df['low'].shift(1) - df['low']
    df['+dm'] = np.where(
        (df['up_move'] > df['down_move']) & (df['up_move'] > 0),
        df['up_move'],
        0.0
    )
    df['-dm'] = np.where(
        (df['down_move'] > df['up_move']) & (df['down_move'] > 0),
        df['down_move'],
        0.0
    )
    
    # Calculate True Range
    df['tr'] = np.maximum(
        df['high'] - df['low'],
        np.maximum(
            abs(df['high'] - df['close'].shift(1)),
            abs(df['low'] - df['close'].shift(1))
    ))
    
    # Smooth the values using Wilder's smoothing method (EMA with alpha=1/period)
    df['+dm_smoothed'] = df['+dm'].ewm(alpha=1/period, adjust=False).mean()
    df['-dm_smoothed'] = df['-dm'].ewm(alpha=1/period, adjust=False).mean()
    df['tr_smoothed'] = df['tr'].ewm(alpha=1/period, adjust=False).mean()
    
    # Calculate +DI and -DI
    df['+di'] = 100 * (df['+dm_smoothed'] / df['tr_smoothed'])
    df['-di'] = 100 * (df['-dm_smoothed'] / df['tr_smoothed'])
    
    # Calculate DX
    df['dx'] = 100 * (abs(df['+di'] - df['-di']) / (df['+di'] + df['-di']))
    
    # Calculate ADX
    df['adx'] = df['dx'].ewm(alpha=1/period, adjust=False).mean()
    
    # Return the relevant columns
    return df[['adx', '+di', '-di']]

Los dos indicadores calculados para el movimiento direccional (+DM, -DM) cuantifican la magnitud de los movimientos alcistas y bajistas de los precios con el fin de evaluar la fuerza de la tendencia. Se calculan a partir de la diferencia entre los máximos consecutivos (movimiento alcista) y su contrario, es decir, la diferencia entre los mínimos consecutivos (movimiento bajista). Esto es importante porque estos son los componentes básicos para +DM y -DM, que a su vez establecen el momento direccional. Al utilizar valores desplazados, garantizamos la comparación entre diferentes períodos. Se asigna un valor a +DM como movimiento ascendente cuando supera el movimiento descendente y es positivo; de lo contrario, +DM es 0. De manera similar, a -DM se le asigna un valor de movimiento descendente cuando supera el movimiento ascendente para que sea positivo. Esto ayuda a filtrar los movimientos no dominantes o negativos.

La función where() de NumPy es eficiente para operaciones vectorizadas, por lo que debe asegurarse de importar NumPy. También es importante verificar que los cálculos de movimientos ascendentes y descendentes sean correctos para evitar clasificar erróneamente los movimientos. El uso de shift(1) introduce NaN para la primera fila que requiere manejo en cálculos posteriores o al devolver resultados. Siempre es fundamental asegurarse de que las columnas "high" y "low" sean numéricas.

El rango verdadero (TR) mide la volatilidad de los precios y ayuda a tener en cuenta las brechas y los rangos intradiarios. El cálculo se realiza tomando el mayor de tres valores: el rango máximo-mínimo, el rango absoluto entre el máximo y el cierre anterior, y el rango absoluto entre el mínimo y el cierre anterior. Esto ayuda a tener en cuenta las diferencias de precios y la volatilidad. Esta medida de la volatilidad de los activos es importante, ya que actúa como denominador para normalizar +DI y -DI. Esto significa que el ADX es representativo de la fuerza de la tendencia en relación con el movimiento de los precios. La función de máximo de NumPy garantiza que se seleccione el rango más grande. También se debe prestar atención al tratamiento de los valores NaN resultantes de la función shift(1).

El método de suavizado de Wilder aplica una media móvil exponencial con un valor alfa concreto para suavizar +DM, -DM y TR. El valor de «alpha» es 1/período, y el uso de «False» para el parámetro «adjust» implica que se da mayor peso a los datos recientes, lo que imita el método original de Wilder. El suavizado ayuda a reducir el ruido, lo que hace que los conjuntos de datos de los indicadores sean más fiables para el análisis de tendencias. La función ewm es eficaz, pero sensible al parámetro alfa. 

Los indicadores direccionales normalizan los movimientos suavizados mediante el rango verdadero para poder comparar la fuerza alcista con la bajista. La escala multiplicada por 100 ayuda a expresarlos como porcentajes. Esto es importante ya que +DI y -DI cuantifican el grado de alcismo o bajismo de una tendencia en relación con la volatilidad. Por lo tanto, los cruces entre +DI y -DI señalan posibles cambios de tendencia. La protección contra la división por cero se puede introducir añadiendo un pequeño valor épsilon. El factor de escala de 100 es estándar para facilitar la interpretación.

El búfer DX determina la diferencia absoluta escalada entre +DI y -DI, dividida por su suma. Esto es fundamental para dimensionar la fuerza relativa del movimiento direccional. El DX es un paso intermedio importante para el ADX, cuya función es capturar la "intensidad" de una tendencia (ya sea alcista o bajista). Se debe tener cuidado al manejar los casos en los que +di + -di son cero para evitar errores de división. Se puede devolver cero o se puede omitir el cálculo.

Finalmente, el ADX calcula el promedio del índice direccional para medir la fuerza de la tendencia general. El suavizado de los valores DX, al utilizar la EMA de Wilder para calcular el ADX, tiende a reflejar la fuerza de la tendencia a largo plazo. Este es el resultado final del indicador, donde los valores superiores a 25 indican una fuerte tendencia, mientras que los inferiores a 20 sugieren un mercado débil o lateral. Para garantizar la coherencia, es importante asegurar valores alfa consistentes en todos los pasos de suavizado.

La instrucción de retorno genera un marco de datos con columnas para 'adx', '+di' y '-di', que son el conjunto completo de búferes de indicadores para el indicador ADX. Esto proporciona a los operadores las métricas relevantes para el análisis de tendencias, con la posibilidad de añadir columnas intermedias de 'dx' o 'tr' para depurar o personalizar el indicador.



Las características

Combinamos las lecturas de estas dos funciones para crear una matriz de señales multidimensional que, en esencia, combina el ADX (para la fuerza de la tendencia) y el CCI (para el impulso) para identificar condiciones específicas del mercado, como el inicio de tendencias, reversiones o estados de sobrecompra/sobreventa. Las señales generadas al combinar estos dos indicadores son lo que denominamos características en un contexto más amplio. Recordemos que en los últimos 4 artículos teníamos 5 conjuntos de datos principales cuando consideramos el emparejamiento de indicadores de la media móvil y el oscilador estocástico. Estas eran características, estados, acciones, recompensas y codificaciones. Estas características son, por lo tanto, el equivalente a las que teníamos entonces. 

La combinación de estos dos indicadores se utiliza bajo la premisa de que se pueden generar 10 patrones característicos a partir de dicha combinación. Podrían ser más, por supuesto, pero para nuestros propósitos esta cifra será suficiente. Estamos asignando una función a cada patrón. Cada función devolverá una matriz NumPy donde cada columna representa una condición (dimensión), donde 1 indica que la condición se cumplió y 0 significa lo contrario. Las funciones tomarán como entrada dataframes de pandas. Estas entradas están etiquetadas como adx_df (con las columnas 'adx', '+di', '-di'); cci_df (con la columna 'cci'); y opcionalmente price_df (con las columnas 'high', 'low', 'close').

Implementamos estas funciones en Python para agilizar el proceso de pruebas, pero también necesitamos una implementación similar en MQL5 para el despliegue/uso de nuestro Asesor Experto final. La gestión integrada de MQL5 de los puntos indicadores mencionados anteriormente en el CCI y el ADX significa que no supondrían un problema cuando se utilice el Asesor Experto. En resumen, para Python, se debe validar adx_df y cci_df para asegurar que las columnas requeridas estén presentes, y también se deben manejar los valores NaN mediante drop o fill para evitar errores en las comparaciones. Las operaciones de desplazamiento, como shift(1), introducen inherentemente valores NaN para la primera fila. Por lo tanto, establecer la primera fila en 0 es una forma estándar de gestionar esto. Idealmente, se deberían introducir operaciones vectorizadas para conjuntos de datos grandes, ya que la función `where()` de NumPy y el método `astype(int)` utilizados pueden resultar insuficientes.

Todas las características fueron probadas/entrenadas en el par EURUSD en el marco temporal diario desde el 1 de enero de 2020 hasta el 1 de enero de 2024. El período walk-forward fue del 1 de enero de 2024 al 1 de enero de 2025. Solo las características 2, 3 y 4 superaron la prueba walk-forward, por lo que sus resultados se comparten junto con sus respectivas descripciones.



Característica-0

Este es un patrón basado en ADX > 25 y cruces de CCI en ±100. Proporciona una confirmación del impulso de ruptura o el inicio de una tendencia. El nivel 25 es significativo para el ADX, por lo tanto, siempre que se cruce junto con el nivel clave CCI de 100, existe una alta probabilidad de que se produzca una nueva tendencia. El uso de estos 2 indicadores ayuda a filtrar el ruido. Siempre es importante, al buscar una tendencia, esperar a que el ADX pruebe el nivel de 25. Debe evitarse cualquier valor inferior a 20. Es una configuración de alta probabilidad en activos conocidos por tener tendencias, como algunos índices bursátiles, etc.

El vector de características generado a partir de estas señales es un vector de 6 dimensiones. Nuestra implementación en Python y MQL5 es la siguiente:

def feature_0(adx_df, cci_df):
    """
    Creates a 3D signal array with the following dimensions:
    1. ADX > 25 crossover (1 when crosses above 25, else 0)
    2. CCI > +100 crossover (1 when crosses above +100, else 0)
    3. CCI < -100 crossover (1 when crosses below -100, else 0)
    """
    # Initialize empty array with 3 dimensions and same length as input
    feature = np.zeros((len(adx_df), 6))
    
    # Dimension 1: ADX > 25 crossover
    feature[:, 0] = (adx_df['adx'] > 25).astype(int)
    feature[:, 1] = (adx_df['adx'].shift(1) <= 25).astype(int)
    
    # Dimension 2: CCI > +100 crossover
    feature[:, 2] = (cci_df['cci'] > 100).astype(int)
    feature[:, 3] = (cci_df['cci'].shift(1) <= 100).astype(int)
    
    # Dimension 3: CCI < -100 crossover
    feature[:, 4] = (cci_df['cci'] < -100).astype(int)
    feature[:, 5] = (cci_df['cci'].shift(1) >= -100).astype(int)
    
    # Set first row to 0 (no previous values to compare)
    feature[0, :] = 0
    
    return feature
   if(Index == 0)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 2)
      {  _features[0] = (_adx[0] > 25 ? 1.0f : 0.0f);
         _features[1] = (_adx[1] <= 25 ? 1.0f : 0.0f);
         _features[2] = (_cci[0] > 100 ? 1.0f : 0.0f);
         _features[3] = (_cci[1] <= 100 ? 1.0f : 0.0f);
         _features[4] = (_cci[0] < -100 ? 1.0f : 0.0f);
         _features[5] = (_cci[1] >= -100 ? 1.0f : 0.0f);
      }
   }

Hemos desglosado nuestro vector de entrada MLP para este patrón en sus señales constituyentes clave. Estos son: ADX previamente por debajo de 25, luego ADX por encima de 25, CCI previamente por debajo de +100, CCI ahora por encima de +100, CCI previamente por encima de -100, CCI actualmente por debajo de -100. Con esta configuración, obviamente no todas las situaciones pueden ser ciertas al mismo tiempo. Lo que nos permite es personalizar todos nuestros datos de precios en lugar de agruparlos según la lógica de patrones típica.

Tradicionalmente, una configuración alcista se daría cuando el ADX cruza desde por debajo de 25 para cerrar por encima de ese nivel, seguido de que el CCI también cruce desde por debajo de +100 para cerrar por encima de +100. De igual modo, el patrón bajista se daría si el ADX volviera a cruzar el nivel de 25 para cerrar por encima de él, pero el CCI cruzara el nivel de -100 desde arriba para cerrar por debajo de él. Si generáramos vectores estrictos que solo probaran estas configuraciones alcistas o bajistas "verdaderas", entonces nuestro vector de entrada tendría un tamaño de 3 con menos variabilidad en los vastos datos de prueba. Nuestra opción elegida de datos de entrada de 6 dimensiones captura estas métricas tradicionales, pero también datos "continuos" que de otro modo serían ignorados por la configuración más "discreta" o "tradicional".



Característica-1

Este patrón evoluciona en torno a ADX > 25 y cruces de CCI de los niveles ±50 de lados opuestos. Se trata de una reentrada impulsada por el impulso dentro de una tendencia ya establecida. Es ideal para operaciones de continuación de retroceso, dado que el ADX confirma la integridad de la tendencia y el CCI detecta la recuperación después de una corta contratendencia. Este patrón es adecuado para los seguidores de tendencias que buscan entrar en una tendencia después de un retroceso. El cruce del CCI en el límite cero es también una señal importante que no debe pasarse por alto. También se pueden colocar stops dinámicos con esta señal para los operadores que ya están en la tendencia y buscan proteger sus ganancias. Nuestra implementación en Python y MQL5 es la siguiente:

def feature_1(adx_df, cci_df):
    """
    Creates a modified 3D signal array with:
    1. ADX > 25 (1 when above 25, else 0)
    2. CCI crosses from below 0 to above +50 (1 when condition met, else 0)
    3. CCI crosses from above 0 to below -50 (1 when condition met, else 0)
    """
    # Initialize empty array with 3 dimensions
    feature = np.zeros((len(adx_df), 5))
    
    # Dimension 1: ADX above 25 (continuous, not just crossover)
    feature[:, 0] = (adx_df['adx'] > 25).astype(int)
    
    # Dimension 2: CCI crosses from <0 to >+50
    feature[:, 1] = (cci_df['cci'] > 50).astype(int)
    feature[:, 2] = (cci_df['cci'].shift(1) < 0).astype(int)
    
    # Dimension 3: CCI crosses from >0 to <-50
    feature[:, 3] = (cci_df['cci'] < -50).astype(int)
    feature[:, 4] = (cci_df['cci'].shift(1) > 0).astype(int)
    
    # Set first row to 0 (no previous values to compare)
    feature[0, :] = 0
    
    return feature
   else if(Index == 1)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 2)
      {  _features[0] = (_adx[0] > 25 ? 1.0f : 0.0f);
         _features[1] = (_cci[0] > 50 ? 1.0f : 0.0f);
         _features[2] = (_cci[1] < 0 ? 1.0f : 0.0f);
         _features[3] = (_cci[0] < -50 ? 1.0f : 0.0f);
         _features[4] = (_cci[1] > 0 ? 1.0f : 0.0f);
      }
   }

Nuestra función genera un vector de salida de 5 dimensiones de valores binarios, cuyos componentes son: si ADX está por encima del nivel 25, si el CCI anterior estaba por encima de 0; si el CCI actual está por debajo o es igual a -50; si el CCI anterior estaba por debajo de 0; si el CCI actual está por encima o es igual a 50. Tradicionalmente, una configuración alcista se daría si el ADX está por encima de 25 y el CCI estaba por debajo de cero anteriormente, pero ahora está por encima de 50. Una configuración bajista se daría si el ADX volviera a estar por encima de 25 y el CCI estuviera por encima de 0 anteriormente, pero ahora esté por debajo o igual a -50. Los puntos mencionados anteriormente sobre la transición hacia un mapeo más continuo/regresivo que uno discreto también son aplicables aquí.



Característica-2

Este patrón se basa en un ADX > 25 con divergencias en el precio y el CCI. Esto también constituye una divergencia o un cambio de tendencia. Utiliza el patrón clásico de divergencia de impulso con el ADX como filtro de tendencia. Esto indica una posible reversión, incluso mientras la tendencia se mantenga activa. Este patrón sería adecuado para situaciones en las que se combine con la acción del precio o con niveles de soporte/resistencia para una mejor confirmación. También resulta ideal en situaciones donde la divergencia se produce después de un movimiento prolongado. Sin embargo, se recomienda precaución, ya que la divergencia suele ser solo una señal temprana, puesto que muchas de ellas fallan si provienen de una tendencia muy fuerte. Nuestra implementación en Python y MQL5 es la siguiente:

def feature_2(adx_df, cci_df, price_df):
    """
    Creates a 5D signal array with:
    1. ADX > 25 (1 when above 25, else 0)
    2. Lower low (1 when current low < previous low, else 0)
    3. Higher CCI (1 when current CCI > previous CCI, else 0)
    4. Higher high (1 when current high > previous high, else 0)
    5. Lower CCI (1 when current CCI < previous CCI, else 0)
    """
    # Initialize empty array with 5 dimensions
    feature = np.zeros((len(price_df), 5))
    
    # Dimension 1: ADX above 25
    feature[:, 0] = (adx_df['adx'] > 25).astype(int)
    
    # Dimension 2: Lower low
    feature[:, 1] = (price_df['low'] < price_df['low'].shift(1)).astype(int)
    
    # Dimension 3: Higher CCI
    feature[:, 2] = (cci_df['cci'] > cci_df['cci'].shift(1)).astype(int)
    
    # Dimension 4: Higher high
    feature[:, 3] = (price_df['high'] > price_df['high'].shift(1)).astype(int)
    
    # Dimension 5: Lower CCI
    feature[:, 4] = (cci_df['cci'] < cci_df['cci'].shift(1)).astype(int)
    
    # Set first row to 0 (no previous values to compare)
    feature[0, :] = 0
    
    return feature
   else if(Index == 2)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 2 && CopyRates(Symbol(),Period(),T, 2, _r) >= 2)
      {  _features[0] = (_adx[0] > 25 ? 1.0f : 0.0f);
         _features[1] = (_r[0].low <= _r[1].low ? 1.0f : 0.0f);
         _features[2] = (_cci[0] > _cci[1] ? 1.0f : 0.0f);
         _features[3] = (_r[0].high > _r[1].high ? 1.0f : 0.0f);
         _features[4] = (_cci[0] < _cci[1] ? 1.0f : 0.0f);
      }
   }

La salida de nuestra función es un vector de 6 dimensiones que consta de: si el ADX está por encima de 25; si los precios mínimos han bajado; si el CCI ha subido; si los precios máximos han subido; y finalmente si el CCI ha bajado. Normalmente, una configuración alcista consistiría en que el ADX estuviera por encima de 25, con el precio registrando mínimos más bajos en un impulso creciente, como lo indica el CCI. De manera similar, en la configuración bajista, el ADX seguiría por encima de 25, pero con el precio marcando máximos más altos, mientras que el CCI registra un descenso.

r2

c2



Característica-3

Este patrón combina un ADX en alza con un CCI en zonas neutrales. Actúa como una confirmación de tendencia con un CCI en zona neutral. Se centra en el impulso sostenido en una tendencia alcista o bajista. Cuando el CCI se encuentra en una zona neutral (de 0 a +/-100), suele significar que el precio está experimentando movimientos constantes pero no extremos. Suele ser más seguro que las señales de sobrecompra/sobreventa, con menor riesgo de entrada falsa. En mercados menos volátiles, podría interpretarse como una señal de que "la tendencia es tu amiga". Cuando se combina con la alineación de la media móvil o la estructura de precios, puede proporcionar mayor seguridad. Nuestra implementación en Python y MQL5 es la siguiente:

def feature_3(adx_df, cci_df):
    """
    Creates a 3D signal array with:
    1. ADX rising (1 when current ADX > previous ADX, else 0)
    2. CCI between 0 and +100 (1 when in range, else 0)
    3. CCI between 0 and -100 (1 when in range, else 0)
    """
    # Initialize empty array with 3 dimensions
    feature = np.zeros((len(adx_df), 5))
    
    # Dimension 1: ADX rising
    feature[:, 0] = (adx_df['adx'] > adx_df['adx'].shift(1)).astype(int)
    
    # Dimension 2: CCI between 0 and +100
    feature[:, 1] = (cci_df['cci'] > 0).astype(int)
    feature[:, 2] = (cci_df['cci'] < 100).astype(int)
    
    # Dimension 3: CCI between 0 and -100
    feature[:, 3] = (cci_df['cci'] < 0).astype(int)
    feature[:, 4] = (cci_df['cci'] > -100).astype(int)
    
    # Set first row to 0 (no previous ADX value to compare)
    feature[0, :] = 0
    
    return feature
   else if(Index == 3)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 2)
      {  _features[0] = (_adx[0] > _adx[1] ? 1.0f : 0.0f);
         _features[1] = (_cci[0] > 0 ? 1.0f : 0.0f);
         _features[2] = (_cci[1] < 100 ? 1.0f : 0.0f);
         _features[3] = (_cci[0] < 0 ? 1.0f : 0.0f);
         _features[4] = (_cci[1] > -100 ? 1.0f : 0.0f);
      }
   }

Nuestra función genera un vector de 5 dimensiones que se compone de: si ADX ha aumentado; si CCI es superior a 0; si CCI es inferior a +100; si CCI es inferior a 0; y si CCI es superior a -100. La configuración alcista tradicional para esto es un ADX en alza con un CCI por encima de 0 pero por debajo de +100. El patrón bajista invertido también se da cuando el ADX sube, pero el CCI está por debajo de 0 pero por encima de -100. Este patrón pone de relieve un ADX ascendente (no solo > 25). Además, los rangos neutrales del CCI tienden a apuntar a las fases iniciales del desarrollo de la tendencia, a diferencia del cruce extremo de la Característica 0, por ejemplo.

r3



Característica-4

Este patrón utiliza configuraciones donde ADX > 25 con recuperación CCI desde extremos. Es una configuración de swing fallido. Captura las trampas de impulso donde CCI rompe un nivel extremo pero luego no logra continuar. La incorporación de ADX garantiza que no se trate de una situación de alta volatilidad dentro de una consolidación. Este patrón se observa a menudo antes de reversiones o recuperaciones bruscas. Se recomienda su uso en sesiones de negociación volátiles (o eventos impulsados por noticias, como los anuncios de nóminas no agrícolas). La clave aquí, sin embargo, es observar las velas de mecha (barras de pin) en los días de swing fallidos para obtener una confirmación más sólida. Nuestra implementación en Python y MQL5 es la siguiente:

def feature_4(adx_df, cci_df):
    """
    Creates a 3D signal array with:
    1. ADX > 25 (1 when above 25, else 0)
    2. CCI dips below -100 then closes above it (1 when condition met, else 0)
    3. CCI breaks above +100 then closes below it (1 when condition met, else 0)
    """
    feature = np.zeros((len(cci_df), 5))
    
    # Dimension 1: ADX above 25
    feature[:, 0] = (adx_df['adx'] > 25).astype(int)
    
    # Dimension 2: CCI dips below -100 then closes above it
    feature[:, 1] = (cci_df['cci'].shift(1) < -100).astype(int)
    feature[:, 2] = (cci_df['cci'] > -100).astype(int)
    
    # Dimension 3: CCI breaks above +100 then closes below it
    feature[:, 3] = (cci_df['cci'].shift(1) > 100).astype(int)
    feature[:, 4] = (cci_df['cci'] < 100).astype(int)
    
    return feature
   else if(Index == 4)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 2)
      {  _features[0] = (_adx[0] > 25 ? 1.0f : 0.0f);
         _features[1] = (_cci[0] < -100 ? 1.0f : 0.0f);
         _features[2] = (_cci[1] > -100 ? 1.0f : 0.0f);
         _features[3] = (_cci[0] > 100 ? 1.0f : 0.0f);
         _features[4] = (_cci[1] < 100 ? 1.0f : 0.0f);
      }
   }

Nuestra función feature-4 nos da un vector de 5 dimensiones que produce valores binarios de 1 y 0 sobre si: ADX está por debajo de 20; CCI estaba por debajo de -100; CCI ahora está por encima de -100; CCI estaba por encima de +100; y si CCI ahora está por debajo de +100; Una señal alcista típica que busca un cambio de impulso sería, por lo tanto, si el ADX está por debajo de 20 y el CCI se mueve de por debajo de -100 a por encima de -100. Por lo tanto, un patrón bajista de cambio también se daría si el ADX estuviera nuevamente por debajo de 20, lo que indicaría una tendencia predominante débil, y el CCI indicara una disminución en el impulso positivo al pasar de estar por encima de +100 a cerrar por debajo del nivel de +100.

r4

c4



Característica-5

Este patrón simplemente utiliza ADX < 20 con picos CCI extremos. Sirve como precursor de baja volatilidad de un repunte del impulso. Ayuda a detectar repuntes en etapas tempranas al observar los picos de CCI durante una fase de ADX baja. No solo eso, sino que también pretende ayudar a los operadores a establecer una posición antes de que comience una tendencia. Al implementar este patrón, suele ser una buena idea usar paradas estrictas, ya que muchos picos pueden resultar engañosos. Este patrón podría proporcionar una ventaja adicional al combinarse con otros indicadores, como las compresiones de las bandas de Bollinger o las rupturas de volumen. Sin embargo, se adapta mejor a marcos temporales más cortos (por ejemplo, de M15 a H1), ya que estos facilitan mejor las operaciones de impulso rápidas. Nuestra implementación en Python y MQL5 es la siguiente:

def feature_5(adx_df, cci_df):
    """
    Creates a 3D signal array with:
    1. ADX < 20 (1 when below 20, else 0) - indicates weak trend
    2. CCI spikes above 150 (1 when condition met, else 0) - extreme overbought
    3. CCI drops below -150 (1 when condition met, else 0) - extreme oversold
    """
    # Initialize array
    feature = np.zeros((len(cci_df), 5))
    
    # Dimension 1: ADX below 20 (weak trend)
    feature[:, 0] = (adx_df['adx'] < 20).astype(int)
    
    # Dimension 2: CCI spikes above 150 (sudden extreme overbought)
    # Using 2-bar momentum to detect "sudden" spikes
    feature[:, 1] = (cci_df['cci'] > 150).astype(int)
    feature[:, 2] = (cci_df['cci'].shift(1) < 130).astype(int)
    
    # Dimension 3: CCI drops below -150 (sudden extreme oversold)
    # Using 2-bar momentum to detect "sudden" drops
    feature[:, 3] = (cci_df['cci'] < -150).astype(int)
    feature[:, 4] = (cci_df['cci'].shift(1) > -130).astype(int)
    
    return feature
   else if(Index == 5)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 2)
      {  _features[0] = (_adx[0] < 20? 1.0f : 0.0f);
         _features[1] = (_cci[0] > 150 ? 1.0f : 0.0f);
         _features[2] = (_cci[1] < 130 ? 1.0f : 0.0f);
         _features[3] = (_cci[0] < -150 ? 1.0f : 0.0f);
         _features[4] = (_cci[1] > -130 ? 1.0f : 0.0f);
      }
   }

Nuestra función de características 5 genera un vector de 5 dimensiones para alimentar nuestra MLP. Las señales capturadas son: si el ADX está por debajo de 20; si el CCI está por encima de +150; si el CCI había estado previamente por debajo de +130; si el CCI está por debajo de -150; y finalmente si el CCI había estado por encima de -130. Utiliza el indicador ADX, que se sitúa por encima de 25, para asegurar que existe una tendencia fuerte, tiene como objetivo detectar la recuperación del CCI desde niveles extremos y, en general, se centra en los cambios de tendencia o de impulso.

Una configuración alcista típica se daría cuando el ADX está por encima de 25, el CCI había estado por debajo de +130 y ahora está en +150. De manera similar, una configuración bajista también requeriría que el ADX estuviera por encima de 25, con el CCI ahora en -150, después de haber probado previamente -130.



Característica-6

Esta función trata sobre el cruce del ADX por debajo de 40 con los filtros de cruce CCI. Este patrón, que indica el agotamiento de la tendencia, se caracteriza por una caída del ADX que señala un debilitamiento de la tendencia. Una vez que el CCI también vuelva a la posición neutral o al lado opuesto, esto también presagia una pérdida de impulso. Puede interpretarse como una señal de reducción de riesgos que sirve como indicador para ajustar el stop loss dinámico o para tomar ganancias. También se puede combinar con patrones de velas japonesas para lograr salidas más limpias; sin embargo, no es recomendable abrir nuevas operaciones con esta configuración. Nuestra implementación en Python y MQL5 es la siguiente:

def feature_6(adx_df, cci_df):
    """
    Creates a 3D signal array with:
    1. ADX crosses below 40 (1 when crosses down, else 0)
    2. CCI crosses below +100 (1 when crosses down, else 0)
    3. CCI crosses above -100 (1 when crosses up, else 0)
    """
    # Initialize array with zeros
    feature = np.zeros((len(cci_df), 6))
    
    # Dimension 1: ADX crosses below 40
    feature[:, 0] = (adx_df['adx'] < 40).astype(int)
    feature[:, 1] = (adx_df['adx'].shift(1) >= 40).astype(int)
    
    # Dimension 2: CCI crosses below +100
    feature[:, 2] = (cci_df['cci'] < 100).astype(int)
    feature[:, 3] = (cci_df['cci'].shift(1) >= 100).astype(int)
    
    # Dimension 3: CCI crosses above -100
    feature[:, 4] = (cci_df['cci'] > -100).astype(int)
    feature[:, 5] = (cci_df['cci'].shift(1) <= -100).astype(int)
    
    return feature
   else if(Index == 6)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 2)
      {  _features[0] = (_adx[0] < 40? 1.0f : 0.0f);
         _features[1] = (_adx[1] < 40? 1.0f : 0.0f);
         _features[2] = (_cci[0] < 100 ? 1.0f : 0.0f);
         _features[3] = (_cci[1] >= 100 ? 1.0f : 0.0f);
         _features[4] = (_cci[0] > -100 ? 1.0f : 0.0f);
         _features[5] = (_cci[1] <= -100 ? 1.0f : 0.0f);
      }
   } 

Nuestra función característica 6 genera un vector de 6 dimensiones que comprende: si ADX ahora está por debajo de 40; si ADX había estado previamente por encima de 40; si CCI está por debajo de 100; si CCI había estado previamente por encima de 100; si CCI está por encima de -100; y finalmente si CCI había estado previamente por debajo de -100. Un patrón de terminación bajista (el equivalente a un patrón alcista) se produce cuando el ADX cae de 40 a menos de 40 y el CCI había estado por debajo de -100 pero ahora está por encima de ese valor. Por el contrario, el patrón de terminación alcista (equivalente a bajista) se produce cuando el ADX vuelve a caer desde 40 y el CCI también desciende desde +100 para caer por debajo de ese nivel. Se recomienda principalmente como señal de salida o aviso de salida; sin embargo, aquí se incluye como señal de entrada únicamente con fines de prueba.



Característica-7

Este caso implica ADX > 25 con filtros divisores de línea cero CCI. Se trata de un indicador que detecta tendencias una vez que el CCI cruza la línea cero. Esto se debe a que el cruce de la línea cero por parte del CCI actúa como un punto de inflexión del impulso. Dado que el ADX también confirma su fortaleza, esta configuración ofrece puntos de entrada claros en la mitad de la tendencia. Este patrón tiende a ser más fiable cuando el precio alcanza máximos más altos o mínimos más bajos. Podría valer la pena considerar la posibilidad de introducir este patrón como señal en múltiples puntos. Se deben realizar pruebas retrospectivas para la alineación temporal o la ventana de volatilidad de la sesión. Nuestra implementación en Python y MQL5 es la siguiente:

def feature_7(adx_df, cci_df):
    """
    Creates Feature-7 3D signal array with:
    1. ADX > 25 (1 when above 25, else 0) - trend strength
    2. CCI crosses above 0 (1 when bullish crossover, else 0)
    3. CCI crosses below 0 (1 when bearish crossover, else 0)
    """
    # Initialize array with zeros
    feature = np.zeros((len(cci_df), 5))
    
    # Dimension 1: ADX above 25 (continuous signal)
    feature[:, 0] = (adx_df['adx'] > 25).astype(int)
    
    # Dimension 2: CCI crosses above 0 (bullish)
    feature[:, 1] = (cci_df['cci'] > 0).astype(int)
    feature[:, 2] = (cci_df['cci'].shift(1) <= 0).astype(int)
    
    # Dimension 3: CCI crosses below 0 (bearish)
    feature[:, 3] = (cci_df['cci'] < 0).astype(int)
    feature[:, 4] = (cci_df['cci'].shift(1) >= 0).astype(int)
    
    return feature
   else if(Index == 7)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 2)
      {  _features[0] = (_adx[0] > 25? 1.0f : 0.0f);
         _features[1] = (_cci[0] > 0? 1.0f : 0.0f);
         _features[2] = (_cci[1] <= 0 ? 1.0f : 0.0f);
         _features[3] = (_cci[0] < 0 ? 1.0f : 0.0f);
         _features[4] = (_cci[1] >= 0 ? 1.0f : 0.0f);
      }
   } 

Nuestra función feature-7 también devuelve un vector de salida de 5 dimensiones. Este vector registra: si ADX es superior a 25; si CCI es superior a 0; si CCI había sido previamente inferior o igual a 0; si CCI es inferior a 0; y finalmente si CCI había sido previamente superior o igual a 0. Los patrones típicos de los que se deriva indican que la señal alcista se produce si el ADX está por encima de 25 y el CCI había estado por debajo de 0 pero ahora está por encima de 0. De manera similar, el patrón bajista se da si el ADX vuelve a estar por encima de 25 y el CCI ahora está por debajo de 0 después de haber estado previamente por encima de 0.


Característica-8

Nuestra novena señal de característica se basa en ADX < 20 con retrocesos extremos de CCI. Se trata, en esencia, de un filtro de fuerza ADX más un indicador de reversión de sobrecompra/sobreventa CCI. Una inversión de rango clásica que es filtrada por el ADX. En mercados con tendencias débiles o con fuertes fluctuaciones, las reversiones del CCI tienden a tener un mejor desempeño. Lo ideal sería utilizarlo únicamente cuando el ADX sea realmente bajo (por debajo de 20) y no aplicarlo en mercados con tendencia definida. La combinación con las bandas de Bollinger o el RSI puede ser adecuada para la confirmación mediante múltiples indicadores. Este patrón de reversión podría ser ideal para activos con tendencia a la reversión a la media, como las materias primas o los pares de divisas. Nuestra implementación en Python y MQL5 es la siguiente:

def feature_8(adx_df, cci_df):
    """
    Creates a 3D signal array with:
    1. ADX < 20 (1 when below 20, else 0) - weak trend
    2. CCI rises from -200 to -100 (1 when condition met, else 0) - extreme oversold bounce
    3. CCI falls from +200 to +100 (1 when condition met, else 0) - extreme overbought pullback
    """
    # Initialize array with zeros
    feature = np.zeros((len(cci_df), 5))
    
    # Dimension 1: ADX below 20 (weak trend)
    feature[:, 0] = (adx_df['adx'] < 20).astype(int)
    
    # Dimension 2: CCI rises from -200 to -100
    # Using 2-bar lookback to detect the move
    feature[:, 1] = (cci_df['cci'] > -100).astype(int)
    feature[:, 2] = (cci_df['cci'].shift(1) <= -200).astype(int)
    
    # Dimension 3: CCI falls from +200 to +100
    # Using 2-bar lookback to detect the move
    feature[:, 3] = (cci_df['cci'] < 100).astype(int)
    feature[:, 4] = (cci_df['cci'].shift(1) >= 200).astype(int)
    
    return feature
   else if(Index == 8)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 2)
      {  _features[0] = (_adx[0] < 20? 1.0f : 0.0f);
         _features[1] = (_cci[0] > -100? 1.0f : 0.0f);
         _features[2] = (_cci[1] <= -200 ? 1.0f : 0.0f);
         _features[3] = (_cci[0] < 100 ? 1.0f : 0.0f);
         _features[4] = (_cci[1] >= 200 ? 1.0f : 0.0f);
      }
   }

La salida de la función Feature-8 también es un vector de 5 dimensiones de valores binarios. Estos, marque: si ADX es inferior a 20; CCI es superior a -100; CCI fue inferior a -200; CCI es inferior a +100; fue superior a +200. Por lo tanto, la señal alcista de la que se derivan estos patrones se produce cuando el ADX está por debajo de 20 y el CCI cruza por encima de -100 después de haber probado previamente el nivel de -200. Por el contrario, el patrón bajista se produce cuando el ADX vuelve a estar por debajo de 20 y el CCI cruza por debajo de +100 después de haber estado previamente por encima de +200.



Característica-9

Nuestra última función utiliza, de nuevo, el ADX > 25 con CCI Delayed Crossings. Este patrón representa el rechazo de señales opuestas o un filtro de trampa. Es experta en detectar rupturas falsas o cambios engañosos en contra de la tendencia dominante. En estas situaciones, a menudo el precio sugiere un cambio de tendencia; sin embargo, el CCI rechaza esta propuesta al reafirmar la tendencia predominante. Es adecuado para la protección contra la trampa de tendencias. Es combinable con la confirmación mediante velas japonesas o la caída del volumen tras una falsa señal. Ideal para operadores que han sufrido pérdidas por señales falsas previas y necesitan un filtro de confianza. Nuestra implementación en Python y MQL5 es la siguiente:

def feature_9(adx_df, cci_df):
    feature = np.zeros((len(cci_df), 7))
    cci = cci_df['cci'].values
    
    # Dimension 1
    feature[:, 0] = (adx_df['adx'] > 25).astype(int)
    
    # Dimension 2: Below 0 then above +50 within 20 periods
    feature[:, 1] = (cci < 0).astype(int)
    feature[:, 2] = (np.roll(cci, 1) >= 0).astype(int)
    below_zero = (feature[:, 1]==1) & (feature[:, 2]==1)
    feature[:, 3] = 0
    for i in np.where(below_zero)[0]:
        if i+20 < len(cci) and np.max(cci[i+1:i+21]) > 50:
            feature[:, 3] = 1
            break
    
    # Dimension 3: Above 0 then below -50 within 20 periods
    feature[:, 4] = (cci > 0).astype(int)
    feature[:, 5] = (np.roll(cci, 1) <= 0).astype(int)
    feature[:, 6] = 0
    above_zero = (feature[:, 4]==1) & (feature[:, 5]==1)
    for i in np.where(above_zero)[0]:
        if i+20 < len(cci) and np.min(cci[i+1:i+21]) < -50:
            feature[:, 6] = 1
            break
    
    return feature
   else if(Index == 9)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 21)
      {  _features[0] = (_adx[0] > 25? 1.0f : 0.0f);
         _features[1] = (_cci[0] < 0? 1.0f : 0.0f);
         _features[2] = (_cci[1] >= 0 ? 1.0f : 0.0f);
         _features[3] = (_cci[ArrayMaximum(_cci,1,20)] > 50 ? 1.0f : 0.0f);
         _features[4] = (_cci[0] > 0? 1.0f : 0.0f);
         _features[5] = (_cci[1] <= 0 ? 1.0f : 0.0f);
         _features[6] = (_cci[ArrayMinimum(_cci,1,20)] < -50 ? 1.0f : 0.0f);
      }
   } 

Este patrón genera un vector de 7 dimensiones que representa: si el ADX está por encima de 25; si el CCI está por debajo de 0; si el CCI anterior estuvo por encima de 0; si en un período de 20 barras anteriores a la última, el CCI estuvo por encima del nivel 50; si el CCI está por encima de 0; si el CCI anterior estuvo por debajo de 0; y finalmente, si en un período de 20 barras anteriores a la última, el CCI cayó por debajo de -50.

Los patrones de señal indicados a partir de los cuales se generan estas señales son los siguientes. Para que haya una señal alcista, el ADX debe estar por encima de 25, y el CCI debe haber vuelto a probar un nivel positivo por encima de 0 después de haber caído por debajo de él con un máximo de hasta 50 en 20 barras antes de la caída. Asimismo, un patrón bajista se da cuando el CCI acaba de caer por debajo de 0 después de haber estado previamente por encima de este valor, con otro mínimo de al menos -50 en las 20 barras anteriores al repunte por encima de 0. Los resultados de las pruebas para este patrón no avanzaron, por lo que no se comparten, pero todo el código fuente se adjunta al final para realizar pruebas independientes adicionales.



Conclusión

Hemos analizado los patrones conjuntos de ADX y CCI en una MLP de aprendizaje supervisado, con resultados entre mixtos y deficientes en la prueba walk-forward. Se trataba de un intento de lograr que el vector de entrada fuera más continuo y menos discreto, como hicimos en el artículo 57 sobre la media móvil y el oscilador estocástico. Aunque esto pueda ser la causa, mantendremos este enfoque en los próximos artículos, ya que también veremos cómo se pueden utilizar otros métodos de aprendizaje automático con estos indicadores.

Nombre Descripción
61_0.onnx MLP para el patrón 0
61_1.onnx MLP para el patrón 1
61_2.onnx MLP para el patrón 2
61_3.onnx MLP para el patrón 3
61_4.onnx MLP para el patrón 4
61_5.onnx MLP para el patrón 5
61_6.onnx MLP para el patrón 6
61_7.onnx MLP para el patrón 7
61_8.onnx MLP para el patrón 8
61_9.onnx MLP para el patrón 9
61_x.mqh Archivo de procesamiento de patrones
SignalWZ_61.mqh Archivo de clase de señal
wz_61.mq5 Asesor experto ensamblado con el Asistente MQL5 (Wizard MQL5) para mostrar los archivos incluidos

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

Archivos adjuntos |
61_0.onnx (265.8 KB)
61_1.onnx (264.8 KB)
61_2.onnx (264.8 KB)
61_3.onnx (264.8 KB)
61_4.onnx (264.8 KB)
61_5.onnx (264.8 KB)
61_6.onnx (265.8 KB)
61_7.onnx (264.8 KB)
61_8.onnx (264.8 KB)
61_9.onnx (266.8 KB)
61_X.mqh (5.67 KB)
SignalWZ_61.mqh (15.85 KB)
wz_61.mq5 (8.06 KB)
Utilizando redes neuronales en MetaTrader Utilizando redes neuronales en MetaTrader
En el artículo se muestra la aplicación de las redes neuronales en los programas de MQL, usando la biblioteca de libre difusión FANN. Usando como ejemplo una estrategia que utiliza el indicador MACD se ha construido un experto que usa el filtrado con red neuronal de las operaciones. Dicho filtrado ha mejorado las características del sistema comercial.
Procesos gaussianos en aprendizaje automático: modelo de regresión en MQL5 Procesos gaussianos en aprendizaje automático: modelo de regresión en MQL5
En este artículo, analizaremos los conceptos básicos de los procesos gaussianos (GP) como modelo de aprendizaje automático probabilístico y demostraremos su aplicación a problemas de regresión utilizando datos sintéticos.
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.
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 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
El asesor experto (EA) «Market Structure Flip Detector» actúa como su socio vigilante, observando constantemente los cambios en el sentimiento del mercado. Mediante el uso de umbrales basados en el rango verdadero medio (ATR), detecta eficazmente los cambios de estructura y señala cada «máximo más bajo» y cada «mínimo más alto» con indicadores claros. Gracias a la rápida ejecución de MQL5 y a su API flexible, esta herramienta ofrece análisis en tiempo real que ajusta la visualización para una legibilidad óptima y proporciona un panel de control en directo para supervisar el número de cambios de estructura y los tiempos. Además, las notificaciones de sonido y push personalizables le garantizan que se mantenga informado de las señales críticas, lo que le permite comprobar cómo unos datos sencillos y unas rutinas de apoyo pueden transformar los movimientos de los precios en estrategias prácticas.