English Русский 中文 Español Deutsch 日本語
preview
Técnicas do MQL5 Wizard que você deve conhecer (Parte 61): Uso de padrões do ADX e CCI com aprendizado supervisionado

Técnicas do MQL5 Wizard que você deve conhecer (Parte 61): Uso de padrões do ADX e CCI com aprendizado supervisionado

MetaTrader 5 — Sistemas de negociação |
31 0
Stephen Njuki
Stephen Njuki

Introdução

Continuamos nossa análise de como indicadores que acompanham diferentes aspectos do mercado podem ser combinados entre si e com o aprendizado de máquina para desenvolver um sistema de negociação. Nestes próximos artigos, analisaremos a combinação do oscilador Índice Direcional Médio (ADX) com o Índice de Canal de Commodities (CCI). O ADX é predominantemente um indicador de confirmação de tendência, enquanto o CCI é um indicador de momentum. Já abordamos essas duas características ao analisar os padrões de indicadores individuais em artigos anteriores, como este. Recapitulando, a confirmação de tendência mede a intensidade de uma determinada tendência de preços; uma maior intensidade pode indicar condições mais adequadas para uma entrada. Os indicadores de momentum, por outro lado, medem a velocidade da variação dos preços. Quanto mais rapidamente o preço se movimenta em determinada direção, menor tende a ser a probabilidade de ocorrerem movimentos adversos significativos contra a posição.

logo



Índice de Canal de Commodities (CCI)

Testamos nossas redes, que recebem os sinais dos indicadores como entrada, em Python. Isso ocorre porque, atualmente, o Python oferece uma vantagem significativa de desempenho em comparação com o MQL5. No entanto, como mencionei em artigos recentes, o MQL5 pode alcançar ou se aproximar desse desempenho com o uso do OpenCL. Para aproveitar a velocidade proporcionada pelo OpenCL, porém, é necessário dispor de uma GPU. Por enquanto, quando ambas as linguagens são executadas em CPUs semelhantes, o Python apresenta desempenho claramente superior e, por isso, será utilizado para testar e desenvolver nossos modelos.

O MetaTrader disponibiliza um módulo para Python que permite não apenas carregar no Python os dados de preços fornecidos pela corretora, mas também executar operações diretamente a partir do Python. Mais informações podem ser encontradas aqui e aqui. Portanto, utilizamos parte desses módulos para nos conectar à corretora e obter os dados de preços no Python para nossa MLP com aprendizado supervisionado. Depois de obter esses dados de preços, precisamos definir as funções dos nossos indicadores. Começando pelo CCI, nossa implementação pode ser feita da seguinte forma:

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']]

O CCI é um oscilador que mede o quanto o preço de um ativo se desvia de sua média estatística. À primeira vista, isso pode ser útil para os traders identificarem condições de sobrecompra ou sobrevenda. A função implementada acima recebe um DataFrame do pandas contendo as colunas de preços 'high', 'low' e 'close', além de um período de análise retrospectiva, cujo valor padrão é 14, para calcular os valores do CCI. O cálculo do ‘Typical Price’ determina a média dos preços máximo, mínimo e de fechamento para representar de forma mais abrangente a ação do preço ao longo do período analisado. Ele corresponde à média aritmética desses três preços principais em cada período e ajuda a simplificar os dados de preços em um único valor representativo. Isso contribui para reduzir o ruído decorrente das oscilações intradiárias. Essa etapa é fundamental, pois o CCI se baseia nos desvios em relação a esse preço típico, e o uso dos três valores — máximo, mínimo e fechamento — ajuda a proporcionar uma visão equilibrada da ação do preço. Portanto, é necessário garantir que o DataFrame do pandas que recebe os dados de preços da corretora por meio do módulo do MetaTrader 5 contenha todas essas colunas, pois valores ausentes podem gerar erros.

Uma média móvel simples (SMA) suaviza o preço típico ao longo do período especificado, estabelecendo uma linha de referência. Isso é feito calculando a SMA ao longo do período definido como entrada, cujo padrão é 14, para que ela possa atuar como referência. Essa etapa é importante porque suaviza as oscilações de preço de curto prazo, fornecendo assim um nível de preço ‘normal’ representativo. A função rolling requer uma quantidade suficiente de pontos de dados, pelo menos equivalente ao período de entrada, para calcular uma SMA válida. Se o conjunto de dados for muito curto, os valores NaN iniciais poderão exigir tratamento específico, por meio de funções como dropna(), entre outras.

O desvio médio mede, então, o desvio absoluto médio desses preços típicos representativos em relação à SMA, capturando assim a volatilidade dos preços. Para cada janela, são calculadas as diferenças absolutas entre cada preço típico e a média da respectiva janela e, em seguida, é calculada a média dessas diferenças. Isso é essencial porque o desvio médio quantifica a volatilidade dos preços, necessária para dimensionar o CCI e permitir que ele reflita adequadamente as oscilações típicas do preço de um ativo. Isso também permite realizar comparações entre diferentes ativos. A aplicação da função lambda pode exigir bastante processamento em grandes conjuntos de dados, por isso pode ser recomendável utilizar alternativas vetorizadas ou bibliotecas como a ta-lib. Nesse caso, ainda será necessário importar o NumPy.

A fórmula do CCI combina todos os componentes apresentados acima para calcular um valor, que posteriormente é dimensionado pela constante 0,015 para fins de normalização. A constante 0,015 é um fator de escala padrão utilizado para tentar manter os valores do CCI dentro da faixa de ±100. Isso nem sempre é alcançado, mas esse é o objetivo do fator. Esse elemento é fundamental para a fórmula do CCI, pois transforma os desvios brutos dos preços em um oscilador padronizado. Valores acima de +100 indicariam, então, condições de sobrecompra, enquanto valores abaixo de -100 sugeririam condições de sobrevenda. Ao utilizar essa fórmula, deve-se ficar atento a possíveis erros de divisão por zero caso o desvio médio seja igual a zero. Embora seja raro, esse cenário pode ocorrer quando os preços permanecem estáveis. Nesse caso, um pequeno valor epsilon, por exemplo 1e-10, pode ser adicionado ao denominador, se necessário, para evitar esse problema. 

A instrução return retorna um DataFrame contendo apenas a coluna ‘cci’. Caso sejam necessárias colunas adicionais para depuração ou análises complementares, como 'typical_price', essa instrução pode ser modificada para incluí-las.



Índice de Direção Média (ADX)

O ADX, conforme mencionado anteriormente, mede a força da tendência, independentemente de sua direção. Isso é feito utilizando o Directional Movement Index, composto principalmente por dois buffers (+DI e -DI). Assim como no CCI acima, essa função recebe um DataFrame do pandas contendo as colunas 'high', 'low' e 'close', além de um período de análise retrospectiva, cujo padrão é 14, para calcular os valores de ADX, +DI e -DI. Sua implementação em Python é a seguinte:

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']]

Os dois buffers calculados para o movimento direcional (+DM e -DM) quantificam os movimentos de alta e de baixa dos preços para avaliar a força direcional. Eles são obtidos a partir da diferença entre máximas consecutivas, no caso do movimento de alta (up-move), e, no sentido oposto, da diferença entre mínimas consecutivas, no caso do movimento de baixa (down-move). Isso é importante porque esses valores constituem a base para +DM e -DM, que, por sua vez, determinam o momentum direcional. Ao utilizar valores deslocados, garantimos a comparação entre diferentes períodos. O valor do up-move é atribuído a +DM quando ele é superior ao down-move e positivo; caso contrário, +DM recebe 0. Da mesma forma, -DM recebe o valor do down-move quando este é superior ao up-move e positivo. Isso ajuda a filtrar movimentos negativos ou que não sejam predominantes.

A função where() do NumPy é eficiente para operações vetorizadas; portanto, é necessário garantir que o NumPy esteja importado. Também é importante verificar se os cálculos de up-move e down-move estão corretos para evitar uma classificação incorreta dos movimentos. O uso de shift(1) introduz um valor NaN na primeira linha, que precisa ser tratado nos cálculos subsequentes ou no momento de retornar os resultados. Também é fundamental garantir que as colunas ‘high’ e ‘low’ contenham valores numéricos.

O True Range, TR, mede a volatilidade dos preços e ajuda a considerar tanto os gaps quanto as amplitudes intradiárias. O cálculo é realizado utilizando o maior entre três valores: a amplitude entre máxima e mínima, o valor absoluto da diferença entre a máxima e o fechamento anterior e o valor absoluto da diferença entre a mínima e o fechamento anterior. Isso permite considerar os gaps de preço e a volatilidade. Essa medida da volatilidade do ativo é importante porque atua como denominador na normalização de +DI e -DI. Com isso, o ADX passa a representar a força da tendência em relação ao movimento dos preços. A função maximum do NumPy garante que a maior amplitude seja selecionada. ambém é necessário tratar adequadamente os valores NaN resultantes de shift(1).

A suavização de Wilder aplica uma média móvel exponencial com um alpha específico para suavizar +DM, -DM e TR. O alpha é igual a 1/período, e o uso de False no parâmetro adjust atribui maior peso aos dados recentes, reproduzindo o método original de Wilder. A suavização ajuda a reduzir o ruído, tornando os conjuntos de dados dos indicadores mais confiáveis para a análise de tendências. A função ewm é eficiente, mas sensível ao parâmetro alpha. 

Os indicadores direcionais normalizam os movimentos suavizados pelo True Range para permitir a comparação entre a força compradora e a força vendedora. A multiplicação por 100 permite expressá-los em termos percentuais. Isso é importante porque +DI e -DI quantificam a intensidade dos movimentos de alta e de baixa em relação à volatilidade. Assim, os cruzamentos entre +DI e -DI podem sinalizar possíveis mudanças de tendência. A proteção contra divisão por zero pode ser implementada adicionando um pequeno valor epsilon. O fator de escala 100 é o padrão utilizado para facilitar a interpretação.

O buffer DX determina a diferença absoluta, em escala, entre +DI e -DI, dividida pela soma de ambos. Isso é essencial para quantificar a força relativa do movimento direcional. O DX é uma etapa intermediária importante no cálculo do ADX e tem a função de capturar a ‘intensidade’ de uma tendência, independentemente de ela ser de alta ou de baixa. Deve-se ter cuidado com situações em que +DI + -DI seja igual a zero, para evitar erros de divisão. Nesses casos, pode-se retornar zero ou simplesmente ignorar o cálculo.

Por fim, o ADX calcula a média do Directional Index para avaliar a força geral da tendência. A suavização dos valores de DX, utilizando a EMA de Wilder para calcular o ADX, tende a refletir a força da tendência ao longo de um horizonte mais amplo. Esse é o resultado final do indicador: valores acima de 25 geralmente indicam uma tendência forte, enquanto valores abaixo de 20 tendem a indicar um mercado com tendência fraca ou em consolidação. É importante utilizar valores de alpha consistentes em todas as etapas de suavização para manter a coerência dos cálculos.

A instrução return fornece um DataFrame com as colunas 'adx', '+di' e '-di', que correspondem ao conjunto completo de buffers do indicador ADX. Isso disponibiliza aos traders as principais métricas necessárias para a análise de tendências, sendo também possível adicionar colunas intermediárias, como 'dx' ou 'tr', para fins de depuração ou personalização do indicador.



Os Recursos

Combinamos as leituras dessas duas funções para criar um array multidimensional de sinais que, essencialmente, reúne o ADX, responsável pela força da tendência, e o CCI, responsável pelo momentum, para identificar condições específicas de mercado, como início de tendências, reversões ou situações de sobrecompra e sobrevenda. Os sinais gerados pela combinação desses dois indicadores são o que denominamos recursos no contexto mais amplo desta abordagem. Vale lembrar que, nos quatro artigos anteriores, trabalhamos com cinco conjuntos principais de dados ao analisar a combinação da MA com o oscilador estocástico. Eram eles: características, estados, ações, recompensas e codificações. Portanto, os recursos utilizadas aqui correspondem ao mesmo conceito empregado anteriormente. 

A combinação desses dois indicadores parte da premissa de que podem ser gerados 10 padrões de recursos a partir deles. Naturalmente, poderiam existir mais, mas, para nossos objetivos, essa quantidade será suficiente. Atribuímos uma função a cada padrão Cada função retornará um array NumPy no qual cada coluna representa uma condição (dimensão): 1 indica que a condição foi atendida e 0 indica o contrário. As funções receberão DataFrames do pandas como entrada. Essas entradas são identificadas como adx_df, contendo as colunas 'adx', '+di' e '-di'; cci_df, contendo a coluna 'cci'; e, opcionalmente, price_df, contendo as colunas 'high', 'low' e 'close'.

Implementamos essas funções em Python para acelerar o processo de testes, mas também precisamos de uma implementação equivalente em MQL5 para a implantação e utilização do nosso Expert Advisor final. O tratamento nativo do MQL5 para os indicadores CCI e ADX mencionados acima faz com que esses aspectos não representem um problema durante a utilização do Expert Advisor. Recapitulando, no Python, adx_df e cci_df devem ser validados para garantir que todas as colunas necessárias estejam presentes, e os valores NaN também devem ser tratados, seja por remoção ou preenchimento, para evitar erros nas comparações. Operações de deslocamento, como shift(1), introduzem naturalmente valores NaN na primeira linha. Portanto, definir a primeira linha como 0 é uma forma padrão de tratar essa situação. Para grandes conjuntos de dados, o ideal é utilizar operações vetorizadas, pois o uso de NumPy where() e astype(int) pode não ser suficiente em termos de desempenho.

Todos os recursos foram testadas e treinadas no par EURUSD, no timeframe diário, entre 2020.01.01 e 2024.01.01. O período de forward walk foi de 2024.01.01 a 2025.01.01. Somente os recursos 2, 3 e 4 apresentaram resultados satisfatórios no forward walk e, por isso, seus resultados são apresentados juntamente com suas respectivas descrições.



Recurso-0

Este padrão se baseia em ADX > 25 e nos cruzamentos do CCI nos níveis de ±100. Ele fornece confirmação do momentum de um breakout ou do início de uma tendência. O nível 25 é uma referência importante para o ADX; portanto, quando esse nível é cruzado em conjunto com o nível-chave de 100 do CCI, há maior probabilidade de início de uma nova tendência. A utilização conjunta desses dois indicadores ajuda a filtrar o ruído do mercado. Ao buscar o início de tendências, é sempre importante aguardar o ADX testar o nível 25. Valores abaixo de 20 devem ser evitados. Trata-se de uma configuração com maior probabilidade de ocorrência em ativos conhecidos por apresentar movimentos direcionais, como alguns índices de ações, entre outros.

O vetor de recursos gerado a partir desses sinais possui 6 dimensões. Nossas implementações em Python e MQL5 são as seguintes:

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);
      }
   }

Dividimos o vetor de entrada da nossa MLP para esse padrão em seus principais sinais constituintes. São eles: ADX anteriormente abaixo de 25; ADX agora acima de 25; CCI anteriormente abaixo de +100; CCI agora acima de +100; CCI anteriormente acima de -100; e CCI atualmente abaixo de -100. Com essa configuração, obviamente nem todas as condições podem ser verdadeiras ao mesmo tempo. No entanto, ela nos permite personalizar todos os pontos dos nossos dados de preços, em vez de agrupá-los em torno da lógica típica do padrão.

Tradicionalmente, uma configuração de alta ocorreria quando o ADX cruzasse o nível 25 de baixo para cima e fechasse acima dele, seguido pelo CCI também cruzando o nível +100 de baixo para cima e fechando acima dele. Da mesma forma, o padrão de baixa ocorreria se, novamente, o ADX cruzasse o nível 25 e fechasse acima dele, mas o CCI cruzasse o nível -100 de cima para baixo e fechasse abaixo dele. Se gerássemos vetores estritos que testassem apenas essas configurações de alta ou de baixa consideradas ‘verdadeiras’, nosso vetor de entrada teria dimensão 3, com menor variabilidade ao longo do amplo conjunto de dados de teste. Nossa opção escolhida, com dados de entrada de 6 dimensões, captura essas métricas tradicionais, mas também dados ‘contínuos’ que, de outra forma, seriam ignorados por uma configuração mais ‘discreta’ ou ‘tradicional’.



Recurso-1

Este padrão se baseia em ADX > 25 e nos cruzamentos do CCI nos níveis de ±50 a partir de lados opostos. Trata-se de uma reentrada por momentum em uma tendência já estabelecida. É ideal para operações de continuação após um pullback, considerando que o ADX confirma a integridade da tendência e o CCI identifica a retomada após um breve movimento contrário à tendência. Esse padrão é adequado para seguidores de tendência que buscam entrar após uma retração. O cruzamento do CCI no nível zero também é um sinal importante que não deve ser ignorado. Trailing stops também podem ser utilizados com esse sinal por traders que já estejam posicionados na tendência e busquem proteger os ganhos. Nossas implementações em Python e MQL5 são as seguintes:

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);
      }
   }

Nossa função gera um vetor de saída de 5 dimensões composto por valores binários, cujos elementos indicam: se o ADX está acima do nível 25; se o CCI anterior estava acima de 0; se o CCI atual está abaixo ou igual a -50; se o CCI anterior estava abaixo de 0; e se o CCI atual está acima ou igual a 50. Tradicionalmente, uma configuração de alta ocorreria se o ADX estivesse acima de 25, o CCI estivesse anteriormente abaixo de zero e agora estivesse acima de 50. Uma configuração de baixa ocorreria se, novamente, o ADX estivesse acima de 25, o CCI estivesse anteriormente acima de 0 e agora estivesse abaixo ou igual a -50. Os pontos mencionados anteriormente sobre a adoção de um mapeamento mais contínuo/regressivo, em vez de discreto, também se aplicam aqui.



Recurso-2

Este padrão se baseia em ADX > 25, com o preço e o CCI apresentando divergências. Trata-se também de uma configuração de divergência ou de reversão dentro de uma tendência. Ela utiliza o padrão clássico de divergência de momentum, tendo o ADX como filtro de tendência. Isso indica uma possível reversão, mesmo enquanto a tendência permanece ativa. Esse padrão é adequado para situações em que é combinado com ação do preço ou níveis de suporte/resistência para obter uma confirmação mais consistente. Também é ideal em situações nas quais a divergência se forma após um movimento prolongado. No entanto, recomenda-se cautela, pois a divergência muitas vezes representa apenas um sinal antecipado, já que muitas delas falham quando surgem após uma tendência muito forte. Nossas implementações em Python e MQL5 são as seguintes:

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);
      }
   }

A saída da nossa função é um vetor de 6 dimensões composto por condições que indicam: se o ADX está acima de 25; se as mínimas dos preços se deslocaram para níveis mais baixos; se o CCI se deslocou para níveis mais altos; se as máximas dos preços se deslocaram para níveis mais altos; e, por fim, se o CCI se deslocou para níveis mais baixos. Normalmente, uma configuração de alta seria composta pelo ADX acima de 25, com o preço registrando mínimas mais baixas enquanto o momentum, representado pelo CCI, apresenta alta. Da mesma forma, em uma configuração de baixa, o ADX permaneceria acima de 25, mas o preço registraria máximas mais altas enquanto o CCI apresentaria queda.

r2

c2



Recurso-3

Este padrão combina um ADX crescente com o CCI em zonas neutras. Ele atua como uma confirmação de tendência com o CCI em uma zona neutra. O padrão se concentra na manutenção do momentum em uma tendência de alta ou de baixa. Quando o CCI está em uma zona neutra, entre 0 e ±100, isso geralmente significa que o preço está realizando movimentos consistentes, porém não extremos. Essa configuração tende a ser mais segura do que sinais de sobrecompra ou sobrevenda, apresentando menor risco de entradas falsas. Pode ser considerada uma abordagem baseada no princípio de que ‘a tendência é sua aliada’ em mercados menos voláteis. Quando combinada com o alinhamento de médias móveis ou com a estrutura de preços, pode proporcionar maior segurança. Nossas implementações em Python e MQL5 são as seguintes:

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);
      }
   }

Nossa função gera um vetor de 5 dimensões composto pelas seguintes condições: se o ADX aumentou; se o CCI está acima de 0; se o CCI está abaixo de +100; se o CCI está abaixo de 0; e se o CCI está acima de -100. A configuração tradicional de alta para esse padrão ocorre quando o ADX está subindo e o CCI está acima de 0, porém abaixo de +100. O padrão inverso de baixa também apresenta um ADX crescente, mas com o CCI abaixo de 0 e acima de -100. Esse padrão enfatiza um ADX crescente, e não apenas um ADX > 25. Além disso, as faixas neutras do CCI tendem a identificar as fases iniciais de desenvolvimento de uma tendência, diferentemente, por exemplo, dos cruzamentos em níveis extremos utilizados na Característica-0.

r3



Recurso-4

Este padrão utiliza configurações em que o ADX > 25, com recuperação do CCI a partir de níveis extremos. Trata-se de uma configuração de falha de movimento (failure swing). Ela captura armadilhas de momentum nas quais o CCI rompe um nível extremo, mas posteriormente não consegue dar continuidade ao movimento. A inclusão do ADX ajuda a garantir que não se trate apenas de uma situação de whipsaw dentro de uma consolidação. Esse padrão costuma ser observado antes de reversões ou movimentos bruscos de retorno dos preços. Ele é mais adequado para sessões de negociação voláteis ou eventos impulsionados por notícias, como a divulgação do Non-Farm Payrolls. O ponto principal, porém, é observar candles com pavios pronunciados (pin bars) nos períodos em que ocorre a falha do movimento, buscando uma confirmação mais consistente. Nossas implementações em Python e MQL5 são as seguintes:

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);
      }
   }

Nossa função do Recurso-4 gera um vetor de 5 dimensões, com valores binários de 1 e 0, indicando se: o ADX está abaixo de 20; o CCI estava abaixo de -100; o CCI agora está acima de -100; o CCI estava acima de +100; e se o CCI agora está abaixo de +100.Portanto, um sinal típico de alta que busca identificar uma mudança no momentum ocorreria quando o ADX estivesse abaixo de 20 e o CCI passasse de um nível abaixo de -100 para acima de -100. De forma inversa, um padrão de baixa ocorreria quando, novamente, o ADX estivesse abaixo de 20, indicando que a tendência predominante está fraca, e o CCI sinalizasse uma redução do momentum positivo ao passar de um nível acima de +100 para fechar abaixo de +100.

r4

c4



Recurso-5

Este padrão utiliza simplesmente ADX < 20 com picos extremos do CCI. Ele funciona como um precursor, em condições de baixa volatilidade, de um possível aumento acentuado do momentum. O padrão ajuda a identificar rompimentos em estágio inicial ao observar movimentos bruscos do CCI durante uma fase de ADX baixo. Além disso, seu objetivo é ajudar os traders a se posicionarem antes do início de uma tendência. Ao implementar esse padrão, geralmente é recomendável utilizar stops mais curtos, pois muitos desses picos podem resultar em falsos rompimentos. Esse padrão pode proporcionar uma vantagem adicional quando combinado com outros indicadores, como compressões das Bandas de Bollinger ou rompimentos de volume. No entanto, ele é mais adequado para períodos gráficos menores (por exemplo, de M15 a H1), pois estes favorecem operações rápidas baseadas em momentum. Nossas implementações em Python e MQL5 são as seguintes:

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);
      }
   }

Nossa função do Recurso-5 gera um vetor de 5 dimensões para alimentar nossa MLP. Os sinais capturados são: se o ADX está abaixo de 20; se o CCI está acima de +150; se o CCI estava anteriormente abaixo de +130; se o CCI está abaixo de -150; e, por fim, se o CCI estava anteriormente acima de -130. Ela utiliza o marcador do ADX acima de 25 para assegurar que uma tendência forte esteja em andamento, busca detectar a recuperação do CCI a partir de níveis extremos e, de modo geral, concentra-se em reversões ou mudanças de momentum.

Uma configuração típica de alta ocorreria quando o ADX estivesse acima de 25, o CCI estivesse anteriormente abaixo de +130 e agora atingisse +150. Da mesma forma, uma configuração de baixa também exigiria que o ADX estivesse acima de 25, com o CCI agora em -150, após ter testado anteriormente o nível -130.



Recurso-6

Este recurso se baseia no cruzamento do ADX para abaixo de 40 combinado com cruzamentos do CCI. Como um sinal de saída associado ao esgotamento da tendência, esse padrão é caracterizado por um ADX em queda, indicando enfraquecimento da tendência. Quando o CCI também retorna à zona neutra ou cruza para o lado oposto, isso também sinaliza perda de momentum. Ele pode ser interpretado como um sinal de redução de risco, servindo como referência para o ajuste do trailing stop ou para a realização de lucros. Também pode ser combinado com padrões de candlestick para proporcionar saídas mais precisas. No entanto, a abertura de novas operações enquanto essa configuração estiver presente geralmente não é recomendada. Nossas implementações em Python e MQL5 são as seguintes:

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);
      }
   } 

Nossa função do Recurso-6 gera um vetor de 6 dimensões composto pelas seguintes condições: se o ADX está agora abaixo de 40; se o ADX estava anteriormente acima de 40; se o CCI está abaixo de 100; se o CCI estava anteriormente acima de 100; se o CCI está acima de -100; e, por fim, se o CCI estava anteriormente abaixo de -100. Um padrão de encerramento de baixa (equivalente ao encerramento de uma configuração de alta) ocorre quando o ADX cai de um nível acima de 40 para abaixo de 40 e o CCI, que anteriormente estava abaixo de -100, passa para acima desse nível. Por outro lado, um padrão de encerramento de alta (equivalente ao encerramento de uma configuração de baixa) ocorre quando, novamente, o ADX cai de um nível acima de 40 e o CCI também recua de um nível acima de +100 para abaixo desse patamar. Esse padrão é mais adequado como sinal de saída ou alerta de saída. No entanto, ele foi incluído aqui como sinal de entrada exclusivamente para fins de teste.



Recurso-7

Este recurso envolve ADX > 25 com cruzamentos da linha zero pelo CCI. Ele funciona como um sinal para acompanhar a tendência quando o CCI cruza a linha zero. Isso ocorre porque o cruzamento da linha zero pelo CCI atua como um ponto de inflexão do momentum. Como o ADX também confirma a força da tendência, essa configuração oferece oportunidades mais claras de entrada durante uma tendência já em andamento. Esse padrão tende a ser mais confiável quando o preço está formando máximas mais altas ou mínimas mais baixas. Também pode ser interessante considerar entradas em vários pontos utilizando esse padrão como sinal. Devem ser realizados backtests para avaliar o alinhamento temporal ou as janelas de volatilidade das sessões. Nossas implementações em Python e MQL5 são as seguintes:

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);
      }
   } 

Nossa função do Recurso-7 também retorna um vetor de saída de 5 dimensões. Esse vetor registra: se o ADX está acima de 25; se o CCI está acima de 0; se o CCI estava anteriormente abaixo ou igual a 0; se o CCI está abaixo de 0; e, por fim, se o CCI estava anteriormente acima ou igual a 0. Nos padrões típicos dos quais esse recurso é derivado, o sinal de alta ocorre quando o ADX está acima de 25 e o CCI, anteriormente abaixo de 0, passa para acima de 0. Da mesma forma, o padrão de baixa ocorre quando o ADX novamente está acima de 25 e o CCI, anteriormente acima de 0, passa para abaixo de 0.


Recurso-8

Nosso nono sinal de recurso utiliza ADX < 20 com retrações extremas do CCI. Ele combina um filtro de força baseado no ADX com um indicador de reversão de sobrecompra/sobrevenda baseado no CCI. Trata-se de uma reversão clássica dentro de uma faixa, filtrada pelo ADX. Em mercados com tendência fraca ou sujeitos a movimentos erráticos, as reversões do CCI tendem a apresentar melhor desempenho. Idealmente, esse padrão deve ser utilizado apenas quando o ADX estiver realmente baixo (abaixo de 20) e não deve ser aplicado em mercados com tendência definida. A combinação com Bandas de Bollinger ou RSI pode ser adequada para obter confirmação por meio de múltiplos indicadores. Esse padrão de reversão pode ser particularmente apropriado para ativos com comportamento de reversão à média, como commodities ou pares de moedas. Nossas implementações em Python e MQL5 são as seguintes:

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);
      }
   }

A função do Recurso-8 também gera um vetor de 5 dimensões composto por valores binários. Esses valores indicam: se o ADX está abaixo de 20; se o CCI está acima de -100; se o CCI estava abaixo de -200; se o CCI está abaixo de +100; e se estava anteriormente acima de +200. O sinal de alta do qual esses padrões são derivados ocorre, portanto, quando o ADX está abaixo de 20 e o CCI cruza para acima de -100, após ter testado anteriormente o nível -200. Por outro lado, o padrão de baixa ocorre quando, novamente, o ADX está abaixo de 20 e o CCI cruza para abaixo de +100, após ter estado anteriormente acima de +200.



Recurso-9

Nosso último recurso utiliza, novamente, ADX > 25 com cruzamentos retardados do CCI. Esse padrão representa a rejeição de um sinal contrário ou funciona como um filtro de armadilhas. Ele é adequado para identificar falsos rompimentos ou movimentos enganosos contra a tendência predominante. Nessas configurações, o preço frequentemente sugere uma reversão; no entanto, o CCI rejeita essa possibilidade ao reafirmar a tendência predominante. Esse padrão é adequado para proteção contra armadilhas de tendência. Ele pode ser combinado com confirmação por padrões de candlestick ou com redução do volume após um falso rompimento. É especialmente útil para traders que já foram prejudicados por sinais falsos anteriores e buscam um filtro adicional de confirmação. Nossas implementações em Python e MQL5 são as seguintes:

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);
      }
   } 

Esse padrão gera um vetor de 7 dimensões que mapeia: se o ADX está acima de 25; se o CCI está abaixo de 0; se o CCI anterior estava acima de 0; se, em um período de 20 barras anterior à última, o CCI esteve acima do nível 50; se o CCI está acima de 0; se o CCI anterior estava abaixo de 0; e, por fim, se, em um período de 20 barras anterior à última, o CCI caiu abaixo de -50.

Os padrões de sinal dos quais esses sinais são gerados são os seguintes. Para um sinal de alta, o ADX precisa estar acima de 25, e o CCI precisa ter testado novamente um nível positivo acima de 0 após ter caído abaixo desse nível, tendo atingido no máximo 50 nas 20 barras anteriores à queda. Da mesma forma, um padrão de baixa ocorre quando o CCI acaba de cair abaixo de 0, após ter estado anteriormente acima desse nível, e registrou outra mínima de pelo menos -50 nas 20 barras anteriores ao movimento de alta acima de 0. Os resultados dos testes desse padrão não apresentaram desempenho satisfatório no forward walk e, portanto, não são apresentados. No entanto, todo o código-fonte está anexado ao final para a realização de testes independentes adicionais.



Conclusão

Analisamos os padrões conjuntos do Índice Direcional Médio (ADX) e do Índice de Canal de Commodities (CCI) em uma MLP de aprendizado supervisionado, com resultados que variaram de medianos a fracos no forward walk. Essa foi uma tentativa de tornar o vetor de entrada mais contínuo e menos discreto, assim como fizemos no artigo 57, que abordou a média móvel e o oscilador estocástico. Embora essa abordagem possa ser uma das causas desses resultados, continuaremos utilizando-a nos próximos artigos, nos quais também veremos como outros métodos de aprendizado de máquina podem ser empregados com esses indicadores.

Nome Descrição
61_0.onnx MLP para o padrão 0
61_1.onnx MLP para o padrão 1
61_2.onnx MLP para o padrão 2
61_3.onnx MLP para o padrão 3
61_4.onnx MLP para o padrão 4
61_5.onnx MLP para o padrão 5
61_6.onnx MLP para o padrão 6
61_7.0nnx MLP para o padrão 7
61_8.onnx MLP para o padrão 8
61_9.onnx MLP para o padrão 9
61_x.mqh Arquivo de processamento de padrões
SignalWZ_61.mqh Arquivo da classe de sinais
wz_61.mq5 Expert Advisor montado pelo Assistente MQL5 para mostrar os arquivos incluídos

Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/17910

Arquivos anexados |
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)
Caminhe em novos trilhos: Personalize indicadores no MQL5 Caminhe em novos trilhos: Personalize indicadores no MQL5
Vou agora listar todas as possibilidades novas e recursos do novo terminal e linguagem. Elas são várias, e algumas novidades valem a discussão em um artigo separado. Além disso, não há códigos aqui escritos com programação orientada ao objeto, é um tópico muito importante para ser simplesmente mencionado em um contexto como vantagens adicionais para os desenvolvedores. Neste artigo vamos considerar os indicadores, sua estrutura, desenho, tipos e seus detalhes de programação em comparação com o MQL4. Espero que este artigo seja útil tanto para desenvolvedores iniciantes quanto para experientes, talvez alguns deles encontrem algo novo.
Desenvolvimento do Kit de Ferramentas de Análise de Price Action (Parte 21): Detector de Inversão da Estrutura de Mercado Desenvolvimento do Kit de Ferramentas de Análise de Price Action (Parte 21): Detector de Inversão da Estrutura de Mercado
O Expert Advisor (EA) Market Structure Flip Detector atua como um parceiro atento, monitorando continuamente as mudanças no sentimento do mercado. Utilizando limites baseados no Average True Range (ATR), ele detecta com eficiência as mudanças na estrutura do mercado e identifica cada higher low (HL) e lower high (LH) com marcadores claros. Graças à rápida execução do MQL5 e à flexibilidade de sua API, essa ferramenta realiza análises em tempo real, ajusta a exibição para proporcionar melhor legibilidade e disponibiliza um painel dinâmico para acompanhar a quantidade de flips e os horários/intervalos dos flips. Além disso, notificações sonoras e push personalizáveis garantem que você seja informado sobre sinais importantes, demonstrando como parâmetros simples e funções auxiliares podem transformar movimentos de preço em estratégias acionáveis.
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Automatizando Estratégias de Trading em MQL5 (Parte 16): Rompimento da Faixa da Meia-Noite com Break of Structure (BoS) e Price Action Automatizando Estratégias de Trading em MQL5 (Parte 16): Rompimento da Faixa da Meia-Noite com Break of Structure (BoS) e Price Action
Neste artigo, automatizamos em MQL5 a estratégia de rompimento da faixa da meia-noite com Break of Structure, detalhando o código utilizado para detectar rompimentos e executar operações. Definimos parâmetros precisos de risco para entradas, Stop Loss e Take Profit. Também abordamos o backtesting e a otimização da estratégia para sua aplicação prática no trading.