English Русский 中文 Español Deutsch 日本語
preview
Automatização de estratégias de trading com MQL5 (Parte 1): Sistema Profitunity (Trading Chaos de Bill Williams)

Automatização de estratégias de trading com MQL5 (Parte 1): Sistema Profitunity (Trading Chaos de Bill Williams)

MetaTrader 5Negociação |
219 8
Allan Munene Mutiiria
Allan Munene Mutiiria

Introdução

Neste artigo exploraremos o sistema Profitunity, uma estratégia de trading desenvolvida por Bill Williams, cujo objetivo é extrair lucro do "caos" do mercado, utilizando alguns indicadores-chave, além de mostrar como automatizá-la na linguagem de programação MetaQuotes Language 5 (MQL5). Começaremos com uma visão geral da estratégia e seus princípios fundamentais. Em seguida, passaremos pelo processo de implementação em MQL5, focando na escrita do código para os principais indicadores e na automatização dos sinais de entrada e saída. Depois, testaremos e otimizaremos o sistema, assegurando sua operação em diferentes condições de mercado. E, por fim, faremos as considerações finais, discutindo o potencial e a eficácia do sistema Profitunity para o trading automatizado. Este artigo é composto pelas seguintes partes:

  1. Visão geral do sistema Profitunity
  2. Implementação da estratégia em MQL5
  3. Testes e otimização da estratégia
  4. Considerações finais

Ao final deste artigo, você terá uma compreensão clara de como automatizar o sistema Profitunity com MQL5: desde a implementação de seus principais indicadores até a otimização de seu desempenho. Com isso, você terá em mãos ferramentas que podem fortalecer sua estratégia de trading e aproveitar o "caos" do mercado para aumentar a performance potencial de suas operações. Vamos começar.


Visão geral do sistema Profitunity

O sistema Profitunity, desenvolvido por Bill Williams, utiliza alguns indicadores especiais que nos permitem compreender os movimentos caóticos do mercado e agir de acordo com eles. A estratégia combina recursos de indicadores de tendência e de momento, criando uma metodologia de trading dinâmica com alta velocidade de resposta. O sistema identifica reversões de tendência e acelerações de mercado, ajudando a encontrar setups com alta probabilidade de sucesso. Os principais indicadores utilizados na estratégia:

  • Fractals (Fractals)
  • Alligator (Alligator)
  • Awesome Oscillator (AO)
  • Accelerator Oscillator (AC)

Todos esses indicadores funcionam em conjunto, fornecendo informações essenciais sobre as condições de mercado e oferecendo sinais de entrada e saída. Vamos analisar em mais detalhes as configurações específicas dos indicadores aplicados na estratégia.

Configurações dos indicadores

Indicador Fractals. Identifica pontos de reversão no mercado. Os fractals se formam quando há cinco barras consecutivas, em que a barra central é a mais alta ou a mais baixa. Eles sinalizam o possível início de uma nova tendência ou reversão de preço, ajudando a marcar máximas ou mínimas locais e permitindo antecipar potenciais mudanças de tendência. Quanto às configurações, o período padrão dos fractals é 2 ou 5. Isso significa que o indicador procura padrões em que uma barra está cercada em cada lado por duas barras que são mais altas (para fractals de baixa) ou mais baixas (para fractals de alta) que ela. Veja como isso aparece no gráfico.

FRACTALS

Indicador Alligator. Esse indicador é uma combinação de três médias móveis suavizadas, conhecidas como "mandíbula" (Jaw), "dentes" (Teeth) e "lábios" (Lips): juntas, elas permitem identificar a tendência no mercado. A interação entre essas linhas ajuda a reconhecer se o mercado está em tendência ou em consolidação. Quando as linhas começam a se afastar, isso sinaliza tendência; quando se aproximam, indica que o mercado está em fase de consolidação.

Configurações:

  • Jaw (linha azul): Período 13, suavização em 8 barras
  • Teeth (linha vermelha): Período 8, suavização em 5 barras
  • Lips (linha verde): Período 5, suavização em 3 barras

O indicador nos ajuda a identificar a direção e a duração da tendência, assim como pontos de entrada e saída do mercado. Veja suas configurações no gráfico.

ALLIGATOR

Indicador Awesome Oscillator (AO). Esse indicador é um oscilador de momento que calcula a diferença entre médias móveis simples do preço mediano, com período de 34 e período de 5. Ele ajuda a medir a força e a direção da tendência, exibindo a diferença entre essas duas médias móveis na forma de um histograma. O indicador utiliza as configurações padrão.

Normalmente, o AO é usado para identificar momentum de alta ou de baixa no mercado e detectar reversões de tendência. Um histograma acima da linha zero indica momentum de alta, enquanto um histograma abaixo da linha zero sinaliza momentum de baixa.

Indicador Accelerator Oscillator (AC). Esse indicador é derivado do AO e mostra a aceleração do momentum de mercado. Ele fornece uma visão sobre se o momentum está acelerando ou desacelerando, algo extremamente importante para identificar mudanças de tendência antes mesmo de elas se desenvolverem por completo. O indicador AC oscila em torno da linha zero, alternando entre a zona verde (positiva) e a zona vermelha (negativa). Suas configurações também são as padrão.

O indicador AC é utilizado em conjunto com o AO para confirmar a força do mercado e as mudanças de momentum, trazendo maior confiança antes de entrar em uma operação, garantindo que o mercado esteja se movendo com força suficiente em uma direção. Agora vamos observar as condições de entrada e saída utilizadas no sistema. Os indicadores AO e AC apresentam o seguinte cenário.

AO E AC

Condições de entrada e saída

O sistema utiliza algumas condições específicas de entrada e saída, baseadas nos sinais consecutivos dos indicadores Fractals, Alligator, Accelerator Oscillator (AC) e Awesome Oscillator (AO). Os sinais, quando combinados, garantem que as operações sejam abertas apenas quando o mercado confirma claramente a direção, reduzindo assim o risco de falsos sinais.

Condições de entrada em compra:

  1. Sinal do indicador Fractal: Surge um sinal de fractal de baixa (Fractal Down) quando o movimento do preço forma uma sequência de mínimas ascendentes, o que sugere um possível movimento de reversão para cima.
  2. Rompimento da linha do indicador Alligator: A linha azul do Alligator ("mandíbula") é cruzada de baixo para cima, indicando o início de uma tendência de alta.
  3. Confirmação no indicador Accelerator Oscillator (AC): O AC está na zona verde, o que indica momentum de alta e reforça a força da tendência.
  4. Confirmação no indicador Awesome Oscillator (AO): O histograma do AO cruza a linha zero de baixo para cima, confirmando também o momentum altista.

Condição de compra:

A entrada em compra acontece após o histograma do AO cruzar a linha zero de baixo para cima, confirmando o crescimento dentro do momentum altista. Esse é um sinal de que uma forte tendência de alta está se desenvolvendo, e aqui será o ponto de abertura da posição longa a mercado. Abaixo segue um exemplo de sinal de compra no gráfico do MetaTrader 5.

SINAL DE COMPRA NO MT5

Condições de entrada em venda:

  1. Sinal do indicador Fractal: Surge um sinal de fractal de alta (Fractal Up) quando o movimento do preço forma uma sequência de máximas descendentes, o que sugere um possível movimento de reversão para baixo.
  2. Rompimento da linha do indicador Alligator: A linha azul do Alligator ("mandíbula") é cruzada de cima para baixo, indicando o início de uma tendência de baixa.
  3. Confirmação no indicador Accelerator Oscillator (AC): O AC está na zona vermelha, confirmando um forte momentum de baixa e indicando alta probabilidade de continuidade do movimento descendente.
  4. Confirmação no indicador Awesome Oscillator (AO): O histograma do AO cruza a linha zero de cima para baixo, sinalizando tendência de baixa.

Condição de venda:

A entrada em venda acontece após o histograma do AO cruzar a linha zero de cima para baixo, confirmando o momentum baixista. Isso sinaliza que o mercado provavelmente continuará o movimento descendente, e aqui será o ponto de abertura da posição curta a mercado.

Condições de saída ou reversão:

  1. Reversão da linha do indicador Alligator: Ocorre a mudança de direção da linha verde do Alligator ("lábios"), sugerindo o possível fim da tendência atual. A direção dos "lábios" apontando para o lado oposto indica que o preço pode estar se revertendo ou entrando em consolidação.
  2. Reversão do indicador Accelerator Oscillator (AC): O AC passa da zona verde para a vermelha (ou vice-versa), sinalizando uma possível mudança de momentum. Esse é um sinal precoce de que o momentum de mercado está mudando e que a tendência atual pode estar chegando ao fim.
  3. Reversão do indicador Awesome Oscillator (AO): O histograma do AO cruza a linha zero na direção oposta, fornecendo confirmação adicional de que um possível reverso de tendência está em andamento.

Condição de saída:

Podem ser usados quaisquer ou todos os sinais de saída descritos acima, no entanto, em nosso caso, tomaremos a decisão de encerrar a posição quando os histogramas do AO mudarem para a zona oposta, indicando alteração no momentum do mercado.

Ao combinar os indicadores mencionados, o sistema Profitunity oferece um método poderoso para identificar reversões de mercado e o surgimento de fortes tendências. Na próxima parte, discutiremos como implementar essas condições de entrada e saída em MQL5, garantindo assim a total automatização dessa estratégia.


Implementação da estratégia em MQL5

Após estudar todos os aspectos teóricos da estratégia de trading de Bill Williams chamada Profitunity, finalmente podemos passar para a automatização da teoria e ao desenvolvimento do EA (Expert Advisor) na linguagem MetaQuotes Language 5 (MQL5) para o MetaTrader 5.

Para criar o EA (Expert Advisor), abra seu terminal MetaTrader 5, clique na aba "Serviço" e selecione "Editor MetaQuotes Language", ou simplesmente pressione F4 no teclado. Como alternativa, você pode clicar no ícone do IDE (ambiente de desenvolvimento integrado) na barra de ferramentas. Isso abrirá o Editor MetaQuotes Language, permitindo programar robôs de trading, indicadores técnicos, scripts e bibliotecas de funções.

ABRA O METAEDITOR

Assim que o MetaEditor for aberto, vá até a aba "Arquivo" na barra de ferramentas e clique em "Novo Arquivo", ou simplesmente pressione CTRL + N para criar um novo documento. Como alternativa, você pode clicar no ícone "Criar" na barra de ferramentas. Isso abrirá a janela do Assistente MQL.

CRIE UM NOVO EA

Na janela do assistente, selecione "EA (Expert Advisor) (modelo)" e clique em "Avançar".

MQL WIZARD

Nos parâmetros gerais do EA, insira o nome do seu EA no campo "Nome". Observe que, para indicar ou criar uma pasta que não existe, é necessário usar uma barra invertida antes do nome do EA. Por exemplo, no nosso caso, por padrão está definido "Experts\". Isso significa que nosso EA será criado na pasta "Experts", e poderemos encontrá-lo lá. Os outros campos são bastante diretos, mas você pode acessar o link na parte inferior da janela do assistente para conferir a explicação detalhada.

NOME DO NOVO EA

Após indicar o nome desejado para o arquivo do EA, clique em "Avançar" e depois em "Concluir". Agora, depois de realizar todos os passos acima, estamos prontos para escrever o código e programar nossa estratégia.

Primeiramente, começaremos definindo alguns metadados do EA (Expert Advisor). Isso inclui o nome do EA, informações de copyright e também o link para o site da MetaQuotes. Também especificamos a versão do EA, que está definida como "1.00".

//+------------------------------------------------------------------+
//|              1. PROFITUNITY (TRADING CHAOS BY BILL WILLIAMS).mq5 |
//|      Copyright 2024, ALLAN MUNENE MUTIIRIA. #@Forex Algo-Trader. |
//|                                     https://forexalgo-trader.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, ALLAN MUNENE MUTIIRIA. #@Forex Algo-Trader"
#property link      "https://forexalgo-trader.com"
#property description "1. PROFITUNITY (TRADING CHAOS BY BILL WILLIAMS)"
#property version   "1.00"

Esse código exibirá os metadados do sistema ao carregar o programa. Agora podemos seguir para o próximo passo e adicionar algumas variáveis globais que utilizaremos dentro do programa. Antes de tudo, incluímos a instância de trade usando #include no início do código-fonte. Como resultado, teremos acesso à classe CTrade, que usaremos para criar o objeto de operações. Esse é um passo fundamental, pois será necessário para abrir trades.

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

O pré-processador substituirá a linha #include <Trade/Trade.mqh> pelo conteúdo do arquivo Trade.mqh. Os sinais de menor e maior indicam que o arquivo Trade.mqh será carregado do diretório padrão (geralmente terminal_installation_directory\MQL5\Include). O diretório atual não é incluído na busca. Essa linha pode ser colocada em qualquer parte do programa, mas normalmente todos os includes ficam no início do código-fonte (para melhorar a estrutura do código e facilitar a leitura). A declaração do objeto obj_Trade da classe CTrade nos dará acesso simples aos métodos contidos nessa classe, algo pelo qual devemos agradecer aos desenvolvedores do MQL5.

CLASSE CTRADE

Depois disso, precisamos declarar alguns handlers importantes dos indicadores que usaremos no sistema de trading.

int handle_Fractals = INVALID_HANDLE; //--- Initialize fractals indicator handle with an invalid handle value
int handle_Alligator = INVALID_HANDLE; //--- Initialize alligator indicator handle with an invalid handle value
int handle_AO = INVALID_HANDLE; //--- Initialize Awesome Oscillator (AO) handle with an invalid handle value
int handle_AC = INVALID_HANDLE; //--- Initialize Accelerator Oscillator (AC) handle with an invalid handle value

Aqui definimos as variáveis iniciais que irão conter os handles de cada indicador técnico no programa. Em particular, inicializamos quatro variáveis do tipo integer: "handle_Fractals", "handle_Alligator", "handle_AO" e "handle_AC" com o valor INVALID_HANDLE.

Cada um desses handles funcionará como uma referência para acessar o respectivo indicador em todo o código. Ao atribuir o valor inicial "INVALID_HANDLE", garantimos que cada variável de handle mostre explicitamente um estado inválido até que ocorra a devida inicialização no código posteriormente. Essa configuração evita erros relacionados ao uso de handles não inicializados e ajuda a identificar falhas no carregamento de algum indicador durante o processo de inicialização.

Resumindo, aqui está a função de cada indicador separadamente:

  • "handle_Fractals" armazenará o handle do indicador Fractals;
  • "handle_Alligator" armazenará o handle do indicador Alligator;
  • "handle_AO" armazenará o handle do indicador Awesome Oscillator;
  • "handle_AC" armazenará o handle do indicador Accelerator Oscillator.

Em seguida, precisamos definir e inicializar os arrays e constantes necessários para armazenar e processar os dados dos indicadores utilizados neste EA, que serão obtidos a partir dos handles já inicializados. Vamos fazer isso de forma sequencial, para manter tudo simples e claro para futuras referências.

double fractals_up[]; //--- Array to store values for upward fractals
double fractals_down[]; //--- Array to store values for downward fractals

double alligator_jaws[]; //--- Array to store values for Alligator's Jaw line
double alligator_teeth[]; //--- Array to store values for Alligator's Teeth line
double alligator_lips[]; //--- Array to store values for Alligator's Lips line

double ao_values[]; //--- Array to store values of the Awesome Oscillator (AO)

double ac_color[]; //--- Array to store color status of the Accelerator Oscillator (AC)
#define AC_COLOR_UP 0 //--- Define constant for upward AC color state
#define AC_COLOR_DOWN 1 //--- Define constant for downward AC color state

Começaremos criando dois arrays, "fractals_up" e "fractals_down", que irão armazenar os valores dos fractals de alta e dos fractals de baixa, respectivamente. Esses arrays permitirão rastrear pontos fractais específicos, ajudando a identificar reversões de preço relevantes ou padrões de mercado.

Depois, criamos três arrays, chamados "alligator_jaws", "alligator_teeth" e "alligator_lips", para armazenar os valores das diferentes linhas do indicador Alligator. Ao manter esses valores em arrays separados, conseguimos acompanhar de forma eficiente o estado de cada linha do indicador e utilizá-los na busca por sinais de trading.

Também definimos o array "ao_values", que armazenará os valores do indicador Awesome Oscillator (AO). O AO nos ajudará a identificar o momentum de mercado e as tendências, e manter esses valores registrados permitirá analisar suas mudanças ao longo do tempo e aplicá-las em nossas condições de trading.

Por fim, declaramos o array "ac_color" para armazenar o estado de cor do indicador Accelerator Oscillator (AC). Esse array conterá a informação sobre o movimento de alta ou de baixa do AC em forma de status de cor. Para simplificar esse processo, declaramos duas constantes: "AC_COLOR_UP" (com valor 0) e "AC_COLOR_DOWN" (com valor 1). Essas constantes irão representar os estados de cor do indicador AC, sendo que a cor verde (para cima) indica crescimento do momentum, enquanto a cor vermelha (para baixo) sinaliza desaceleração da tendência. Essa configuração tornará mais simples nossa lógica quando, mais adiante, formos verificar o status do AC para geração de sinais de trading.

Você deve ter notado que conseguimos facilmente armazenar os valores dos nossos indicadores diretamente, exceto no caso dos fractals. Isso acontece porque seus valores estão disponíveis imediatamente para cada barra. Entretanto, no caso dos fractals, eles formam pontos de swing específicos, que aparecem a pelo menos 3 barras de distância da atual. Dessa forma, não podemos obter de maneira direta as barras com fractals, já que eles só se confirmam quando certas condições são atendidas. Por isso, precisamos de uma lógica para rastrear o valor anterior do fractal, assim como sua direção. Aplicamos a seguinte abordagem.

double lastFractal_value = 0.0; //--- Variable to store the value of the last detected fractal
enum fractal_direction {FRACTAL_UP, FRACTAL_DOWN, FRACTAL_NEUTRAL}; //--- Enum for fractal direction states
fractal_direction lastFractal_direction = FRACTAL_NEUTRAL; //--- Variable to store the direction of the last fractal

Aqui definimos variáveis e uma enumeração para armazenar e gerenciar os dados do último fractal detectado em nossa análise de trading. Começamos declarando a variável "lastFractal_value" e a inicializamos com o valor "0.0". Essa variável irá guardar o valor numérico do último fractal identificado no gráfico. Acompanhando esse valor, podemos compará-lo com o preço atual e analisar as formações fractais em busca de potenciais sinais de trading.

Em seguida, definimos a enumeração "fractal_direction" com três possíveis estados: "FRACTAL_UP", "FRACTAL_DOWN" e "FRACTAL_NEUTRAL". Esses estados representam a direção do último fractal:

  • "FRACTAL_UP" indicará um fractal de alta, sinalizando condições potencialmente baixistas.
  • "FRACTAL_DOWN" indicará um fractal de baixa, sinalizando condições potencialmente altistas.
  • "FRACTAL_NEUTRAL" representa a situação em que nenhum direcionamento de fractal foi confirmado.

Por fim, declaramos a variável "lastFractal_direction" do tipo "fractal_direction" e a inicializamos com o valor "FRACTAL_NEUTRAL". Essa variável armazenará a direção do último fractal detectado, permitindo avaliar o sentido do mercado dentro da lógica de trading com base no último fractal identificado.

Agora finalmente podemos passar para a lógica principal de processamento do código. Precisamos inicializar nossos indicadores, e para isso utilizaremos diretamente o manipulador de eventos OnInit, que é chamado e executado a cada vez que o programa é inicializado.
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){
   //---

   //---
   return(INIT_SUCCEEDED); //--- Return successful initialization status
}

Em termos simples, trata-se do manipulador de eventos de inicialização padrão, que usaremos para configurar a lógica de controle do nosso programa. Em seguida, precisamos inicializar os indicadores de forma que fiquem anexados ao gráfico, permitindo que sejam usados para extrair dados e tomar decisões de trading. Primeiro, inicializamos o indicador Fractals, conforme mostrado abaixo.

   handle_Fractals = iFractals(_Symbol,_Period); //--- Initialize the fractals indicator handle
   if (handle_Fractals == INVALID_HANDLE){ //--- Check if the fractals indicator failed to initialize
      Print("ERROR: UNABLE TO INITIALIZE THE FRACTALS INDICATOR. REVERTING NOW!"); //--- Print error if fractals initialization failed
      return (INIT_FAILED); //--- Exit initialization with failed status
   }

Aqui inicializamos a variável "handle_Fractals" por meio da chamada da função iFractals. Essa função cria um handle para o indicador Fractals, aplicado ao símbolo especificado (representado pelo argumento _Symbol) e ao período atual do gráfico (argumento _Period). Ao atribuir à variável "handle_Fractals" o valor retornado pela função iFractals, garantimos acesso aos dados do indicador, que posteriormente poderemos utilizar na análise de formações fractais dentro da estratégia.

Após a tentativa de inicializar o indicador Fractals, verificamos se a inicialização foi bem-sucedida conferindo se a variável "handle_Fractals" tem o valor "INVALID_HANDLE". Esse valor indica que a inicialização do indicador falhou, o que pode ocorrer por diferentes motivos, como falta de recursos do sistema ou parâmetros incorretos.

Se a inicialização não for bem-sucedida, usamos a função Print para registrar a mensagem de erro "ERROR: UNABLE TO INITIALIZE THE FRACTALS INDICATOR. REVERTING NOW!" no log. Essa mensagem deixará o problema claro, facilitando a resolução de falhas. Em seguida, retornamos INIT_FAILED na saída da função OnInit, o que sinaliza que o processo de inicialização não foi concluído corretamente. Essa verificação garante que o EA não continue rodando com configurações incompletas, o que poderia levar a erros de execução. Aplicamos a mesma verificação também para a inicialização do indicador Alligator.

   handle_Alligator = iAlligator(_Symbol,_Period,13,8,8,5,5,3,MODE_SMMA,PRICE_MEDIAN); //--- Initialize the alligator indicator with specific settings
   if (handle_Alligator == INVALID_HANDLE){ //--- Check if the alligator indicator failed to initialize
      Print("ERROR: UNABLE TO INITIALIZE THE ALLIGATOR INDICATOR. REVERTING NOW!"); //--- Print error if alligator initialization failed
      return (INIT_FAILED); //--- Exit initialization with failed status
   }

Inicializamos a variável "handle_Alligator" chamando a função iAlligator. O indicador Alligator exige alguns parâmetros para definir suas três linhas ("mandíbula", "dentes" e "lábios"), cada uma delas reagindo de forma distinta às tendências de mercado. Definimos essas configurações da seguinte maneira: "13" como período da "mandíbula", "8" como período dos "dentes" e "5" como período dos "lábios". Também configuramos os valores de deslocamento "8", "5" e "3" para cada linha, aplicamos o método de cálculo MODE_SMMA (média móvel suavizada) e utilizamos o tipo de preço PRICE_MEDIAN.

Após tentar inicializar o indicador Alligator, verificamos se a variável "handle_Alligator" possui um valor válido. Se ela for igual a INVALID_HANDLE, isso indica que o processo de inicialização falhou. Essa falha pode ter ocorrido devido à falta de recursos ou a parâmetros incorretos, o que impede o funcionamento adequado do Alligator.

Se a inicialização não for bem-sucedida, chamamos a função Print para exibir a mensagem de erro: "ERROR: UNABLE TO INITIALIZE THE ALLIGATOR INDICATOR. REVERTING NOW!" Essa mensagem nos alertará sobre o tipo de problema, facilitando sua identificação e correção. Após exibir a mensagem, retornamos INIT_FAILED na saída da função OnInit, sinalizando que a inicialização não foi concluída corretamente. 

Adotamos a mesma abordagem para a inicialização dos indicadores AO e AC, conforme descrito a seguir.

   handle_AO = iAO(_Symbol,_Period); //--- Initialize the Awesome Oscillator (AO) indicator handle
   if (handle_AO == INVALID_HANDLE){ //--- Check if AO indicator failed to initialize
      Print("ERROR: UNABLE TO INITIALIZE THE AO INDICATOR. REVERTING NOW!"); //--- Print error if AO initialization failed
      return (INIT_FAILED); //--- Exit initialization with failed status
   }
   handle_AC = iAC(_Symbol,_Period); //--- Initialize the Accelerator Oscillator (AC) indicator handle
   if (handle_AC == INVALID_HANDLE){ //--- Check if AC indicator failed to initialize
      Print("ERROR: UNABLE TO INITIALIZE THE AC INDICATOR. REVERTING NOW!"); //--- Print error if AC initialization failed
      return (INIT_FAILED); //--- Exit initialization with failed status
   }

Assim que todos os indicadores forem inicializados com sucesso, poderemos adicioná-los automaticamente ao gráfico quando o programa for carregado. Para isso, utilizamos a seguinte lógica.

   if (!ChartIndicatorAdd(0,0,handle_Fractals)){ //--- Add the fractals indicator to the main chart window and check for success
      Print("ERROR: UNABLE TO ADD THE FRACTALS INDICATOR TO CHART. REVERTING NOW!"); //--- Print error if fractals addition failed
      return (INIT_FAILED); //--- Exit initialization with failed status
   }

Aqui tentamos adicionar o indicador Fractals à janela principal do gráfico utilizando a função ChartIndicatorAdd. Passamos "0" como identificador do gráfico (indicando o gráfico atual) e "0" como identificador da janela, especificando a janela principal. A variável "handle_Fractals", que inicializamos anteriormente para armazenar o handle do indicador Fractals, é então passada para adicionar esse indicador em específico.

Após a chamada da função ChartIndicatorAdd, verificamos se ela foi executada com sucesso. Se a função retornar "false" (no formato "!"), isso significa que não foi possível adicionar o indicador Fractals ao gráfico. Essa falha pode ter ocorrido devido a limitações do gráfico ou à falta de recursos. Nesse caso, exibimos uma mensagem de erro usando a função Print para notificar: "ERROR: UNABLE TO ADD THE FRACTALS INDICATOR TO CHART. REVERTING NOW!" Essa mensagem nos ajudará a identificar rapidamente a origem do problema durante a depuração.

Se não for possível adicionar o indicador, retornamos INIT_FAILED na saída da função OnInit com o status de falha, garantindo que o EA não continue funcionando sem o indicador Fractals presente no gráfico. Isso previne erros de execução, assegurando a disponibilidade visual do indicador. Uma lógica semelhante é usada para adicionar o Alligator, já que ele também é exibido na janela principal, conforme mostrado abaixo.

   if (!ChartIndicatorAdd(0,0,handle_Alligator)){ //--- Add the alligator indicator to the main chart window and check for success
      Print("ERROR: UNABLE TO ADD THE ALLIGATOR INDICATOR TO CHART. REVERTING NOW!"); //--- Print error if alligator addition failed
      return (INIT_FAILED); //--- Exit initialization with failed status
   }

Para adicionar os outros indicadores utilizamos a mesma abordagem, mas com a diferença de que agora alteramos as sub-janelas, já que criamos uma nova sub-janela para cada indicador correspondente, conforme mostrado abaixo.

   if (!ChartIndicatorAdd(0,1,handle_AO)){ //--- Add the AO indicator to a separate subwindow and check for success
      Print("ERROR: UNABLE TO ADD THE AO INDICATOR TO CHART. REVERTING NOW!"); //--- Print error if AO addition failed
      return (INIT_FAILED); //--- Exit initialization with failed status
   }
   if (!ChartIndicatorAdd(0,2,handle_AC)){ //--- Add the AC indicator to a separate subwindow and check for success
      Print("ERROR: UNABLE TO ADD THE AC INDICATOR TO CHART. REVERTING NOW!"); //--- Print error if AC addition failed
      return (INIT_FAILED); //--- Exit initialization with failed status
   }

Adicionamos os indicadores Awesome Oscillator (AO) e Accelerator Oscillator (AC) em sub-janelas separadas no gráfico, garantindo que cada um tenha sua própria visualização dedicada. Para isso, usamos a função ChartIndicatorAdd para cada indicador. Definimos "0" como identificador do gráfico (indicando o gráfico atual) e utilizamos identificadores de janelas distintos: "1" para o indicador AO e "2" para o indicador AC, cada um deles exibido em sua própria sub-janela. A numeração aqui é fundamental, pois cada indicador deve estar em uma janela separada, por isso é necessário garantir a indexação correta das sub-janelas.

Em seguida, verificamos a execução bem-sucedida de cada adição individualmente. Se a função ChartIndicatorAdd retornar "false" para o AO ou para o AC, isso significa que o processo de inclusão falhou. Nesse caso, exibimos uma mensagem de erro com a função Print, especificando qual indicador não foi carregado corretamente. Por exemplo, se não foi possível adicionar o AO, exibimos "ERROR: UNABLE TO ADD THE AO INDICATOR TO CHART. REVERTING NOW!" Da mesma forma, exibimos uma mensagem de erro caso o AC não seja adicionado com sucesso.

Se qualquer um dos indicadores não for adicionado, retornamos imediatamente INIT_FAILED, encerramos a execução da função OnInit e impedimos a continuidade do programa. Para confirmar que tudo ocorreu corretamente, podemos também registrar no log os handles dos indicadores.

   Print("HANDLE ID FRACTALS = ",handle_Fractals); //--- Print the handle ID for fractals
   Print("HANDLE ID ALLIGATOR = ",handle_Alligator); //--- Print the handle ID for alligator
   Print("HANDLE ID AO = ",handle_AO); //--- Print the handle ID for AO
   Print("HANDLE ID AC = ",handle_AC); //--- Print the handle ID for AC

Ao iniciar o programa, obtemos os seguintes dados de inicialização.

DADOS DOS HANDLES DOS INDICADORES

Na imagem, podemos observar que os identificadores dos handles começam em 10 e variam até 13. Os handles são elementos críticos no MQL5, pois permitem que façamos referência a cada indicador em qualquer ponto do ciclo de vida do EA. Quando uma função, como CopyBuffer, busca valores de um indicador, ela depende desses handles para acessar os dados corretos. Nesse exemplo, os números inteiros representam os identificadores de cada indicador inicializado. Cada identificador atua como um "ponteiro" exclusivo no ambiente MQL5, vinculando cada handle ao seu respectivo indicador. Isso possibilita que o EA saiba exatamente quais dados de indicadores extrair da memória, garantindo uma execução eficiente e uma organização clara das tarefas baseadas em indicadores.

Agora, tudo o que precisamos fazer é configurar os arrays para armazenar os dados em forma de séries temporais.

   ArraySetAsSeries(fractals_up,true); //--- Set the fractals_up array as a time series
   ArraySetAsSeries(fractals_down,true); //--- Set the fractals_down array as a time series
   
   ArraySetAsSeries(alligator_jaws,true); //--- Set the alligator_jaws array as a time series
   ArraySetAsSeries(alligator_teeth,true); //--- Set the alligator_teeth array as a time series
   ArraySetAsSeries(alligator_lips,true); //--- Set the alligator_lips array as a time series
   
   ArraySetAsSeries(ao_values,true); //--- Set the ao_values array as a time series
   
   ArraySetAsSeries(ac_color,true); //--- Set the ac_color array as a time series

Aqui configuramos cada array como uma série temporal, ou seja, os dados dentro de cada array ficam organizados do valor mais recente para o mais antigo. Fazemos isso aplicando a função ArraySetAsSeries a cada array e passando "true" como segundo argumento. Essa configuração garante que o último ponto de dado esteja sempre no índice 0, o que é especialmente útil em aplicações de trading, onde o acesso imediato ao valor mais recente é crucial para a tomada de decisões em tempo real.

Começamos configurando os arrays "fractals_up" e "fractals_down" como séries temporais, o que nos permite acompanhar de forma eficiente os últimos valores de fractals de alta e de fractals de baixa. Da mesma forma, aplicamos esse mesmo princípio de organização aos arrays "alligator_jaws", "alligator_teeth" e "alligator_lips", que representam as três linhas do indicador Alligator. Isso nos permitirá acessar os valores mais recentes de cada linha em tempo real, simplificando a detecção de qualquer alteração nas tendências do mercado.

Também configuramos da mesma forma o array "ao_values", que armazena os dados do indicador Awesome Oscillator. Ao defini-lo como série temporal, garantimos que o valor mais recente do oscilador esteja sempre imediatamente disponível para nossos cálculos. Por fim, aplicamos essa estrutura ao array "ac_color", que rastreia o status de cor do indicador Accelerator Oscillator, de modo que o último status de cor também esteja acessível de forma instantânea. Abaixo temos o trecho de código completo responsável por essa inicialização simples e organizada.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){
   //---
   
   handle_Fractals = iFractals(_Symbol,_Period); //--- Initialize the fractals indicator handle
   if (handle_Fractals == INVALID_HANDLE){ //--- Check if the fractals indicator failed to initialize
      Print("ERROR: UNABLE TO INITIALIZE THE FRACTALS INDICATOR. REVERTING NOW!"); //--- Print error if fractals initialization failed
      return (INIT_FAILED); //--- Exit initialization with failed status
   }
   handle_Alligator = iAlligator(_Symbol,_Period,13,8,8,5,5,3,MODE_SMMA,PRICE_MEDIAN); //--- Initialize the alligator indicator with specific settings
   if (handle_Alligator == INVALID_HANDLE){ //--- Check if the alligator indicator failed to initialize
      Print("ERROR: UNABLE TO INITIALIZE THE ALLIGATOR INDICATOR. REVERTING NOW!"); //--- Print error if alligator initialization failed
      return (INIT_FAILED); //--- Exit initialization with failed status
   }
   handle_AO = iAO(_Symbol,_Period); //--- Initialize the Awesome Oscillator (AO) indicator handle
   if (handle_AO == INVALID_HANDLE){ //--- Check if AO indicator failed to initialize
      Print("ERROR: UNABLE TO INITIALIZE THE AO INDICATOR. REVERTING NOW!"); //--- Print error if AO initialization failed
      return (INIT_FAILED); //--- Exit initialization with failed status
   }
   handle_AC = iAC(_Symbol,_Period); //--- Initialize the Accelerator Oscillator (AC) indicator handle
   if (handle_AC == INVALID_HANDLE){ //--- Check if AC indicator failed to initialize
      Print("ERROR: UNABLE TO INITIALIZE THE AC INDICATOR. REVERTING NOW!"); //--- Print error if AC initialization failed
      return (INIT_FAILED); //--- Exit initialization with failed status
   }
   
   if (!ChartIndicatorAdd(0,0,handle_Fractals)){ //--- Add the fractals indicator to the main chart window and check for success
      Print("ERROR: UNABLE TO ADD THE FRACTALS INDICATOR TO CHART. REVERTING NOW!"); //--- Print error if fractals addition failed
      return (INIT_FAILED); //--- Exit initialization with failed status
   }
   if (!ChartIndicatorAdd(0,0,handle_Alligator)){ //--- Add the alligator indicator to the main chart window and check for success
      Print("ERROR: UNABLE TO ADD THE ALLIGATOR INDICATOR TO CHART. REVERTING NOW!"); //--- Print error if alligator addition failed
      return (INIT_FAILED); //--- Exit initialization with failed status
   }
   if (!ChartIndicatorAdd(0,1,handle_AO)){ //--- Add the AO indicator to a separate subwindow and check for success
      Print("ERROR: UNABLE TO ADD THE AO INDICATOR TO CHART. REVERTING NOW!"); //--- Print error if AO addition failed
      return (INIT_FAILED); //--- Exit initialization with failed status
   }
   if (!ChartIndicatorAdd(0,2,handle_AC)){ //--- Add the AC indicator to a separate subwindow and check for success
      Print("ERROR: UNABLE TO ADD THE AC INDICATOR TO CHART. REVERTING NOW!"); //--- Print error if AC addition failed
      return (INIT_FAILED); //--- Exit initialization with failed status
   }
   
   Print("HANDLE ID FRACTALS = ",handle_Fractals); //--- Print the handle ID for fractals
   Print("HANDLE ID ALLIGATOR = ",handle_Alligator); //--- Print the handle ID for alligator
   Print("HANDLE ID AO = ",handle_AO); //--- Print the handle ID for AO
   Print("HANDLE ID AC = ",handle_AC); //--- Print the handle ID for AC

   ArraySetAsSeries(fractals_up,true); //--- Set the fractals_up array as a time series
   ArraySetAsSeries(fractals_down,true); //--- Set the fractals_down array as a time series
   
   ArraySetAsSeries(alligator_jaws,true); //--- Set the alligator_jaws array as a time series
   ArraySetAsSeries(alligator_teeth,true); //--- Set the alligator_teeth array as a time series
   ArraySetAsSeries(alligator_lips,true); //--- Set the alligator_lips array as a time series
   
   ArraySetAsSeries(ao_values,true); //--- Set the ao_values array as a time series
   
   ArraySetAsSeries(ac_color,true); //--- Set the ac_color array as a time series
   
   //---
   return(INIT_SUCCEEDED); //--- Return successful initialization status
}

Em seguida, podemos avançar para o manipulador de eventos OnTick, no qual ficará a lógica de controle.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
//---
}
De forma resumida, trata-se do manipulador de eventos de ticks padrão, que utilizaremos como base para a lógica de execução. Depois, extraímos os valores dos dados a partir dos handles dos indicadores para realizar as análises.
   if (CopyBuffer(handle_Fractals,0,2,3,fractals_up) < 3){ //--- Copy upward fractals data; check if copying is successful
      Print("ERROR: UNABLE TO COPY THE FRACTALS UP DATA. REVERTING!"); //--- Print error message if failed
      return;
   }
   if (CopyBuffer(handle_Fractals,1,2,3,fractals_down) < 3){ //--- Copy downward fractals data; check if copying is successful
      Print("ERROR: UNABLE TO COPY THE FRACTALS DOWN DATA. REVERTING!"); //--- Print error message if failed
      return;
   }

Aqui copiamos os dados dos buffers "fractals_up" e "fractals_down" do indicador "handle_Fractals" para seus respectivos arrays. Usamos a função CopyBuffer para extrair os dados do handle do indicador. Em particular, tentamos copiar 3 pontos de dados, começando a partir da terceira (contando da mais recente) barra (com índice 2), tanto para fractals de alta quanto para fractals de baixa.

Primeiramente, verificamos se a função retorna um valor menor que 3, o que significaria que menos de três valores foram copiados para o array "fractals_up". Se isso acontecer, exibimos uma mensagem de erro ("ERROR: UNABLE TO COPY THE FRACTALS UP DATA. REVERTING!") e encerramos a função para evitar que o programa continue trabalhando com dados incompletos.

Da mesma forma, tentamos copiar os dados dos fractals de baixa para o array "fractals_down" usando a mesma função CopyBuffer. Novamente, se a cópia falhar (retornando menos de três valores), exibimos a mensagem de erro correspondente ("ERROR: UNABLE TO COPY THE FRACTALS DOWN DATA. REVERTING!") e saímos da função para evitar qualquer outro problema. Essa abordagem garante que nosso programa não trabalhe com dados inválidos ou incompletos, mantendo assim a confiabilidade da lógica de trading. Ao verificar se a quantidade correta de valores foi copiada, conseguimos evitar potenciais erros na análise dos fractals, que são extremamente importantes para detectar pontos de reversão de mercado.

No entanto, você deve ter notado que os números dos buffers dos indicadores diferem – 0 e 1. Esses índices são de grande importância e você precisará lidar com eles frequentemente, pois representam os buffers reais que armazenam os valores dos indicadores. A ilustração abaixo mostra por que usamos índices específicos.

ÍNDICES DOS BUFFERS

Na ilustração, vemos que o fractal de alta vem primeiro, portanto tem índice 0, enquanto o fractal de baixa vem em seguida, com índice 1. Pela mesma lógica, mapeamos as linhas do Alligator.

   if (CopyBuffer(handle_Alligator,0,0,3,alligator_jaws) < 3){ //--- Copy Alligator's Jaw data
      Print("ERROR: UNABLE TO COPY THE ALLIGATOR JAWS DATA. REVERTING!");
      return;
   }
   if (CopyBuffer(handle_Alligator,1,0,3,alligator_teeth) < 3){ //--- Copy Alligator's Teeth data
      Print("ERROR: UNABLE TO COPY THE ALLIGATOR TEETH DATA. REVERTING!");
      return;
   }
   if (CopyBuffer(handle_Alligator,2,0,3,alligator_lips) < 3){ //--- Copy Alligator's Lips data
      Print("ERROR: UNABLE TO COPY THE ALLIGATOR LIPS DATA. REVERTING!");
      return;
   }

Aqui, como os buffers se referem às três linhas, você pode perceber que os índices começam em 0, depois 1 e 2. No caso em que existe apenas um buffer, como no indicador AO, teremos apenas o índice 0 para os preços, conforme mostrado abaixo.

   if (CopyBuffer(handle_AO,0,0,3,ao_values) < 3){ //--- Copy AO data
      Print("ERROR: UNABLE TO COPY THE AO DATA. REVERTING!");
      return;
   }

O mesmo ocorreria com o indicador AC, caso estivéssemos interessados em seus valores. No entanto, no nosso caso, só precisamos identificar a cor do histograma formado. Isso pode ser obtido aplicando a mesma lógica de numeração dos buffers, mas nesse caso os buffers de cor geralmente são atribuídos ao próximo índice disponível na janela de dados. Assim, em nosso caso, isso resulta em 0+1=1, como mostrado abaixo.

   if (CopyBuffer(handle_AC,1,0,3,ac_color) < 3){ //--- Copy AC color data
      Print("ERROR: UNABLE TO COPY THE AC COLOR DATA. REVERTING!");
      return;
   }

Na prática, para verificar os buffers de cor, basta abrir a janela de propriedades do indicador, e os índices de cor aparecem na aba de parâmetros.

ÍNDICE DO BUFFER DE COR

Após extrair e armazenar os dados, poderemos utilizá-los para a tomada de decisões de trading. Para economizar recursos, realizaremos as verificações em cada nova barra, mas não em cada tick gerado. Dessa forma, precisamos de uma lógica para identificar a formação de novas barras.

if (isNewBar()){ //--- Check if a new bar has formed

//---

}

Aqui utilizamos uma função personalizada, cujo código está apresentado abaixo.

//+------------------------------------------------------------------+
//|   IS NEW BAR FUNCTION                                            |
//+------------------------------------------------------------------+
bool isNewBar(){ 
   static int prevBars = 0; //--- Store previous bar count
   int currBars = iBars(_Symbol,_Period); //--- Get current bar count for the symbol and period
   if (prevBars == currBars) return (false); //--- If bars haven't changed, return false
   prevBars = currBars; //--- Update previous bar count
   return (true); //--- Return true if new bar is detected
}

Definimos a função booleana "isNewBar", que verifica se uma nova barra apareceu no gráfico, ajudando a determinar quando uma nova barra ou vela foi formada, algo essencial para atualizar e recalcular as condições de trading. Começamos declarando a variável estática "prevBars" do tipo integer e inicializando-a com o valor "0". A palavra-chave "static" garante que o valor de "prevBars" seja preservado entre diferentes chamadas da função, em vez de ser resetado a cada execução. Isso permite armazenar a quantidade de barras após a última chamada da função.

Em seguida, declaramos a variável inteira local "currBars" e usamos a função interna iBars para obter a quantidade atual de barras do símbolo selecionado (_Symbol) e do período (_Period). Essa função contabiliza o total de barras disponíveis para o timeframe definido e armazena o resultado em "currBars".

Depois, comparamos as variáveis "prevBars" e "currBars". Se os dois valores forem iguais, isso significa que nenhuma nova barra foi formada desde a última execução da função. Nesse caso, retornamos "false", indicando que não há novas barras. Se a quantidade de barras mudou (ou seja, uma nova barra foi formada), a condição não será atendida e atribuímos à variável "prevBars" o novo valor de "currBars", atualizando a referência. Por fim, retornamos "true", indicando que uma nova barra foi detectada.

Agora, dentro dessa função, podemos identificar e registrar dados sobre fractals, verificando e atualizando o valor e a direção do último fractal detectado com base nas informações armazenadas nos arrays "fractals_up" e "fractals_down".

const int index_fractal = 0;
if (fractals_up[index_fractal] != EMPTY_VALUE){ //--- Detect upward fractal presence
   lastFractal_value = fractals_up[index_fractal]; //--- Store fractal value
   lastFractal_direction = FRACTAL_UP; //--- Set last fractal direction as up
}
if (fractals_down[index_fractal] != EMPTY_VALUE){ //--- Detect downward fractal presence
   lastFractal_value = fractals_down[index_fractal];
   lastFractal_direction = FRACTAL_DOWN;
}

Começamos declarando a constante inteira "index_fractal" e atribuímos a ela o valor 0. Essa constante representará o índice dos dados atuais de fractals que desejamos verificar. Nesse caso, analisamos o primeiro fractal nos arrays.

Em seguida, verificamos a condição de que o elemento "fractals_up[index_fractal]" não seja igual a EMPTY_VALUE. Se essa condição for verdadeira, significa que existe um fractal de alta válido. Nesse caso, armazenamos o valor do fractal de alta na variável "lastFractal_value". Também atribuímos à variável "lastFractal_direction" o valor "FRACTAL_UP", para indicar que o último fractal detectado foi um fractal de alta.

De forma semelhante, verificamos se o valor do elemento "fractals_down[index_fractal]" é diferente de EMPTY_VALUE, o que indica a presença de um fractal de baixa. Se essa condição for verdadeira, armazenamos o valor do fractal de baixa na variável "lastFractal_value" e atribuimos à variável "lastFractal_direction" o valor "FRACTAL_DOWN", para indicar que o último fractal detectado foi um fractal de baixa. Em seguida, podemos registrar os dados no log e verificar sua validade.

if (lastFractal_value != 0.0 && lastFractal_direction != FRACTAL_NEUTRAL){ //--- Ensure fractal is valid
   Print("FRACTAL VALUE = ",lastFractal_value);
   Print("FRACTAL DIRECTION = ",getLastFractalDirection());
}

Como resultado, os dados do indicador Fractals serão gravados no log. Criamos uma função personalizada para obter a direção do fractal, cujo código é apresentado abaixo.

//+------------------------------------------------------------------+
//|     FUNCTION TO GET FRACTAL DIRECTION                            |
//+------------------------------------------------------------------+

string getLastFractalDirection(){
   string direction_fractal = "NEUTRAL"; //--- Default direction set to NEUTRAL
   
   if (lastFractal_direction == FRACTAL_UP) return ("UP"); //--- Return UP if last fractal was up
   else if (lastFractal_direction == FRACTAL_DOWN) return ("DOWN"); //--- Return DOWN if last fractal was down
   
   return (direction_fractal); //--- Return NEUTRAL if no specific direction
}

Aqui definimos a função "getLastFractalDirection", que serve para identificar e retornar a direção do último fractal detectado. A função funciona verificando o valor da variável "lastFractal_direction", que rastreia se o último fractal foi de alta ou de baixa. Começamos inicializando a variável "direction_fractal" do tipo string e atribuímos a ela o valor padrão "NEUTRAL". Isso significa que, caso nenhum fractal válido seja detectado, ou se a direção não mudar, a função retornará "NEUTRAL".

Depois, verificamos o valor da variável "lastFractal_direction". Se for igual a "FRACTAL_UP" (o que indica que o último fractal detectado foi de alta), a função retornará a string "UP". Se a variável "lastFractal_direction" tiver o valor "FRACTAL_DOWN" (o que indica que o último fractal detectado foi de baixa), a função retornará a string "DOWN". Se nenhuma das condições for atendida (ou seja, não foi detectado nem fractal de alta nem fractal de baixa, permanecendo no estado neutro), a função retornará o valor padrão "NEUTRAL", indicando que, até o momento, não há direção definida.

Também podemos registrar no log os dados dos demais indicadores, como mostrado abaixo.

Print("ALLIGATOR JAWS = ",NormalizeDouble(alligator_jaws[1],_Digits));
Print("ALLIGATOR TEETH = ",NormalizeDouble(alligator_teeth[1],_Digits));
Print("ALLIGATOR LIPS = ",NormalizeDouble(alligator_lips[1],_Digits));

Print("AO VALUE = ",NormalizeDouble(ao_values[1],_Digits+1));

if (ac_color[1] == AC_COLOR_UP){
   Print("AC COLOR UP GREEN = ",AC_COLOR_UP);
}
else if (ac_color[1] == AC_COLOR_DOWN){
   Print("AC COLOR DOWN RED = ",AC_COLOR_DOWN);
}

Após a execução, obteremos o seguinte resultado:

Confirmação do indicador Fractals:

CONFIRMAÇÃO DO INDICADOR FRACTALS

Confirmação dos demais indicadores:

CONFIRMAÇÃO DOS DEMAIS INDICADORES

A partir da visualização, podemos notar que os dados extraídos correspondem exatamente às informações exibidas na janela de dados, o que confirma que o processo foi bem-sucedido. No próximo passo, poderemos utilizar esses dados para trading. Primeiro, como mostrado abaixo, definimos algumas funções necessárias que usaremos para as análises.

//+------------------------------------------------------------------+
//|        FUNCTION TO GET CLOSE PRICES                              |
//+------------------------------------------------------------------+

double getClosePrice(int bar_index){
   return (iClose(_Symbol, _Period, bar_index)); //--- Retrieve the close price of the specified bar
}

//+------------------------------------------------------------------+
//|        FUNCTION TO GET ASK PRICES                                |
//+------------------------------------------------------------------+

double getAsk(){
   return (NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits)); //--- Get and normalize the Ask price
}

//+------------------------------------------------------------------+
//|        FUNCTION TO GET BID PRICES                                |
//+------------------------------------------------------------------+

double getBid(){
   return (NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits)); //--- Get and normalize the Bid price
}

Aqui definimos três funções para obter o preço de fechamento, o preço Ask e o preço Bid, respectivamente. Em seguida, precisamos declarar variáveis booleanas para verificar potenciais sinais de trading com base na linha da mandíbula do indicador Alligator, conforme mostrado abaixo.

bool isBreakdown_jaws_buy = alligator_jaws[1] < getClosePrice(1) //--- Check if breakdown for buy
                            && alligator_jaws[2] > getClosePrice(2);
bool isBreakdown_jaws_sell = alligator_jaws[1] > getClosePrice(1) //--- Check if breakdown for sell
                            && alligator_jaws[2] < getClosePrice(2);

Primeiro, definimos "isBreakdown_jaws_buy" para identificar a condição de rompimento que pode gerar um sinal de compra. A condição é que o valor do elemento do array "alligator_jaws" no índice 1 (que representa a barra anterior) seja menor que o preço de fechamento da barra anterior, obtido pela função "getClosePrice(1)". Além disso, o valor do elemento do array "alligator_jaws" no índice 2 (que representa a barra imediatamente anterior ao já citado) deve ser maior que o preço de fechamento da barra correspondente, obtido pela função "getClosePrice(2)". Essa combinação sugere que a linha da mandíbula do Alligator cruzou para baixo o preço de fechamento da barra anterior, mas permaneceu acima do preço de fechamento da barra precedente, o que pode ser interpretado como um possível setup de compra.

Depois, definimos "isBreakdown_jaws_sell" para identificar a condição de rompimento que pode gerar um sinal de venda. Nesse caso, o valor do elemento do array "alligator_jaws" no índice 1 deve ser maior que o preço de fechamento da barra anterior, enquanto o valor do elemento "alligator_jaws" no índice 2 deve ser menor que o preço de fechamento da barra precedente. Esse cenário sugere que a linha da mandíbula do Alligator cruzou para cima o nível de preço de fechamento da barra anterior, mas permaneceu abaixo do preço de fechamento da barra precedente, o que pode ser interpretado como um possível setup de venda. A partir daí, podemos definir as demais condições para abertura de posições.

if (lastFractal_direction == FRACTAL_DOWN //--- Conditions for Buy signal
   && isBreakdown_jaws_buy
   && ac_color[1] == AC_COLOR_UP
   && (ao_values[1] > 0 && ao_values[2] < 0)){
   Print("BUY SIGNAL GENERATED");
   obj_Trade.Buy(0.01,_Symbol,getAsk()); //--- Execute Buy order
}
else if (lastFractal_direction == FRACTAL_UP //--- Conditions for Sell signal
   && isBreakdown_jaws_sell
   && ac_color[1] == AC_COLOR_DOWN
   && (ao_values[1] < 0 && ao_values[2] > 0)){
   Print("SELL SIGNAL GENERATED");
   obj_Trade.Sell(0.01,_Symbol,getBid()); //--- Execute Sell order
}

Aqui implementamos a lógica de execução dos sinais de compra e venda com base na combinação de indicadores, em especial a direção do fractal, o rompimento da mandíbula do Alligator, o status de cor do Accelerator Oscillator (AC) e os valores do Awesome Oscillator (AO).

Primeiro, verificamos se as condições para um sinal de compra são atendidas. Confirmamos se "lastFractal_direction" tem o valor "FRACTAL_DOWN", ou seja, se o último fractal identificado foi um fractal de baixa. Em seguida, verificamos se a condição "isBreakdown_jaws_buy" é verdadeira e, caso seja, isso indica que a linha da mandíbula do Alligator rompeu o nível de preço para baixo, configurando um setup para uma possível compra.

Além disso, verificamos se "ac_color[1]" é igual a "AC_COLOR_UP", o que significa que o indicador Accelerator Oscillator está em status de cor ascendente, sinalizando um sentimento altista no mercado. Por fim, analisamos os valores do indicador Awesome Oscillator: o elemento "ao_values[1]" deve ser maior que zero (indicando momentum positivo), enquanto o elemento "ao_values[2]" deve ser menor que zero (indicando momentum negativo no período anterior). Essa combinação sugere que ocorreu uma reversão de momentum, na qual o mercado passou da zona negativa para a positiva. Se todas essas condições forem atendidas, é gerado um sinal de compra, e executamos uma ordem de compra com o tamanho de lote definido (0,01) ao preço Ask.

Por outro lado, verificamos se as condições para um sinal de venda são atendidas. Confirmamos se "lastFractal_direction" tem o valor "FRACTAL_UP", ou seja, se o último fractal identificado foi um fractal de alta. Em seguida, verificamos se a condição "isBreakdown_jaws_sell" é verdadeira e, se for, isso indica que a linha da mandíbula do Alligator cruzou o nível de preço para cima, configurando um setup para uma possível venda.

Além disso, verificamos se "ac_color[1]" é igual a "AC_COLOR_DOWN", o que significa que o indicador Accelerator Oscillator está em status de cor descendente, sinalizando um sentimento baixista no mercado. Por fim, analisamos os valores do indicador Awesome Oscillator: o elemento "ao_values[1]" deve ser menor que zero (indicando momentum negativo), enquanto o elemento "ao_values[2]" deve ser maior que zero (indicando momentum positivo no período anterior). Essa combinação sugere que o mercado está revertendo de momentum positivo para negativo. Se todas essas condições forem atendidas, é gerado um sinal de venda, e executamos uma ordem de venda com o tamanho de lote definido (0,01) ao preço Bid.

Como resultado, as posições serão abertas. No entanto, será possível perceber que não há ordens de saída, e, consequentemente, as posições permanecerão em aberto. Por isso, precisamos de uma lógica para o fechamento das posições, baseada nos sinais de reversão do indicador AO.

if (ao_values[1] < 0 && ao_values[2] > 0){ //--- Condition to close all Buy positions
   if (PositionsTotal() > 0){
      Print("CLOSE ALL BUY POSITIONS");
      for (int i=0; i<PositionsTotal(); i++){
         ulong pos_ticket = PositionGetTicket(i); //--- Get position ticket
         if (pos_ticket > 0 && PositionSelectByTicket(pos_ticket)){ //--- Check if ticket is valid
            ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
            if (pos_type == POSITION_TYPE_BUY){ //--- Close Buy positions
               obj_Trade.PositionClose(pos_ticket);
            }
         }
      }
   }
}
else if (ao_values[1] > 0 && ao_values[2] < 0){ //--- Condition to close all Sell positions
   if (PositionsTotal() > 0){
      Print("CLOSE ALL SELL POSITIONS");
      for (int i=0; i<PositionsTotal(); i++){
         ulong pos_ticket = PositionGetTicket(i); //--- Get position ticket
         if (pos_ticket > 0 && PositionSelectByTicket(pos_ticket)){ //--- Check if ticket is valid
            ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
            if (pos_type == POSITION_TYPE_SELL){ //--- Close Sell positions
               obj_Trade.PositionClose(pos_ticket);
            }
         }
      }
   }
}

Aqui implementamos a lógica para fechar todas as posições ativas (tanto longas quanto curtas) com base nos valores do indicador Awesome Oscillator (AO). Primeiro, verificamos a condição para encerrar todas as posições de compra. Se o elemento "ao_values[1]" for menor que zero e o elemento "ao_values[2]" for maior que zero, isso indica uma possível mudança de momentum de positivo para negativo. Esse é o critério para o fechamento de todas as posições longas. Para implementar isso, começamos verificando se existem posições abertas utilizando a função PositionsTotal.

Se existirem posições abertas, percorremos todas elas em um loop, extraindo o número do ticket de cada posição com a função PositionGetTicket. Para cada posição, validamos o ticket utilizando a função PositionSelectByTicket, garantindo que a posição seja válida. Em seguida, obtemos o tipo da posição por meio da função PositionGetInteger e o armazenamos no enumerador ENUM_POSITION_TYPE. Se a posição for do tipo POSITION_TYPE_BUY, isso significa que se trata de uma posição longa, e então a fechamos com "obj_Trade.PositionClose(pos_ticket)", exibindo a mensagem "CLOSE ALL BUY POSITIONS" para confirmação.

Depois verificamos a condição de fechamento de todas as posições de venda. Se o elemento "ao_values[1]" for maior que zero e o elemento "ao_values[2]" menor que zero, isso indica uma possível reversão de momentum de negativo para positivo, sinalizando a necessidade de encerrar todas as posições curtas. Da mesma forma, primeiro verificamos se há posições abertas. Caso existam, percorremos cada uma em um loop, extraímos os tickets, validamos os tickets e verificamos o tipo de posição. Se a posição for do tipo POSITION_TYPE_SELL, isso significa que se trata de uma posição curta, e então a fechamos com "obj_Trade.PositionClose(pos_ticket)", exibindo a mensagem "CLOSE ALL SELL POSITIONS" para confirmação.

Assim que executarmos o programa, obteremos o seguinte resultado.

POSIÇÃO DE COMPRA

Tudo ocorreu com sucesso. Podemos observar que confirmamos e abrimos a posição longa quando todas as condições de entrada foram atendidas. Com isso, a implementação da estratégia está concluída. Agora precisamos testar o programa no testador de estratégias e otimizá-lo, caso seja necessário adaptá-lo às condições atuais do mercado. Vamos tratar disso na próxima seção.


Testes e otimização da estratégia

Após a implementação dos principais aspectos, o próximo passo será testar nosso EA (Expert Advisor) utilizando o testador de estratégias do MetaTrader 5, a fim de avaliar com precisão sua eficácia em diferentes cenários de mercado. O objetivo dessa etapa de teste é verificar se a estratégia se comporta de acordo com nossas expectativas, além de identificar os ajustes necessários para otimizar os resultados. Nesta fase, já realizamos uma otimização inicial, com foco especial nos parâmetros essenciais da estratégia.

Dedicamos atenção especial aos valores-limite dos fractals e às linhas do Alligator para avaliar a resposta do EA em diferentes sessões de trading e sob distintas condições de mercado. Esse processo de teste minucioso nos permitiu garantir que o programa reage aos sinais esperados de compra e venda, gerenciando as operações de forma eficaz, maximizando a confiabilidade e a eficiência, ao mesmo tempo em que minimiza potenciais erros. Abaixo estão apresentados os resultados obtidos durante os testes.

Resultados do backtest:

RESULTADOS

Gráfico do backtest:

GRÁFICO


Considerações finais

Neste artigo, abordamos o processo de criação de um EA (Expert Advisor) em MQL5 com base na estratégia de trading Profitunity, que utiliza fractals, o indicador Alligator e os osciladores Awesome Oscillator e Accelerator Oscillator para identificar sinais estratégicos de compra e venda. Partindo dos principais indicadores e condições-limite, automatizamos os sinais de trading fundamentados no momentum do mercado e em rompimentos de preço. Cada etapa envolveu a escrita criteriosa do código, a configuração dos handles dos indicadores e a implementação da lógica de abertura de ordens de compra e venda conforme os critérios da estratégia. Após a conclusão da implementação, realizamos testes detalhados no testador de estratégias do MetaTrader 5 para garantir a resposta adequada e a confiabilidade em diferentes condições de mercado, com foco na precisão da execução das ordens por meio de parâmetros otimizados.

Disclaimer: Este artigo tem caráter educacional e serve como guia para a construção de um programa baseado em sinais de trading da estratégia Profitunity. As estratégias e métodos apresentados não garantem resultados específicos e devem ser utilizados com cautela. Sempre realize testes completos e verifique a adaptação das soluções automatizadas às condições atuais do mercado e à sua tolerância pessoal ao risco.

Este artigo oferece uma abordagem estruturada para a automatização de sinais de trading em MQL5 através da estratégia Profitunity. Esperamos que ele incentive você a continuar explorando o desenvolvimento em MQL5 e o inspire a criar sistemas de trading mais complexos e lucrativos. Boa programação e bons trades!

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

Últimos Comentários | Ir para discussão (8)
Allan Munene Mutiiria
Allan Munene Mutiiria | 5 ago. 2025 em 09:04
MrBrooklin iniciante em programação MQL5, não está muito claro para mim por que é necessário inicializar os identificadores de todos os indicadores com o valor INVALID_HANDLE de uma só vez? O que acontecerá se declararmos os handles dos indicadores sem inicialização? O Expert Advisor não funcionará ou o quê?

Atenciosamente, Vladimir.

Obrigado por seu comentário. Não é obrigatório inicializar as alças, mas é uma boa prática de programação fazê-lo, pois você pode verificar se elas foram inicializadas depois de defini-las para evitar possíveis erros. Isso é apenas uma verificação de segurança. Por exemplo, você pode fazer isso:

//--- em um escopo global
int m_handleRsi; // IDENTIFICADOR NÃO INICIALIZADO
OR
int m_handleRsi = INVALID_HANDLE; // IDENTIFICADOR INICIALIZADO


//--- na inicialização
m_handleRsi = iRSI(m_symbol, RSI_TF, RSI_PERIOD, RSI_APP_PRICE); // VOCÊ PODERIA SIMPLESMENTE INICIALIZAR E SEGUIR EM FRENTE
OR
m_handleRsi = iRSI(m_symbol, RSI_TF, RSI_PERIOD, RSI_APP_PRICE); // VOCÊ PODERIA INICIALIZAR E VERIFICAR. ISTO É MELHOR
if (m_handleRsi == INVALID_HANDLE) {
   Print("Failed to initialize RSI indicator");
   return false;
}

// Agora qualquer um funcionará. Vamos ver um exemplo em que a inicialização do indicador falha, embora seja raro.
// Se não houver verificação, nenhum indicador será adicionado e, portanto, a lógica da estratégia será adulterada.
// Para quem verificou, o programa será encerrado, evitando a estratégia falsa. No manipulador de eventos OnInit, ele retornará falha na inicialização e o programa não será executado.
// Assim, o usuário saberá que algo falhou e precisa ser verificado. Se você não tiver verificado, o programa será executado, mas onde ele precisar do indicador de falha, a lógica falhará. Você entendeu agora?
// A lógica de inicialização tem a seguinte aparência:

int OnInit() {
   if (!(YOUR LOGIC) e.g. m_handleRsi == INVALID_HANDLE) {
      return INIT_FAILED;
   }
   return INIT_SUCCEEDED;
}

Isso faz sentido agora? Obrigado.

Zhang Ming
Zhang Ming | 30 ago. 2025 em 09:14
Conteúdo muito detalhado, obrigado por compartilhá-lo maravilhosamente!
peter matty
peter matty | 31 ago. 2025 em 06:53

Citação: Neste artigo, examinamos o Sistema Profitunity de Bill Williams, detalhando seus principais componentes e sua abordagem exclusiva para negociar no caos do mercado.

Resposta: As colunas de lucros e perdas só existirão se seuproduto testado ou o mercado estável for tão bom quanto o mercado a termo que você está usando em relação à carteira subsequente ou à cesta de índices que seguirá essa linha de ordem.

Existem alguns índices e ETFs recém-fundados que estão sendo lançados, ou que são produzidos cada vez mais, para esse uso pretendido e que produzirão esses resultados, margens de lucro, como o índice dowjones 30, bem como muitos outros índices que foram criados para esse uso pretendido. Peter Matty

Miguel Angel Vico Alba
Miguel Angel Vico Alba | 31 ago. 2025 em 11:43
peter matty #:

O artigo não trata de "colunas" de lucros/perdas ou índices de mercado/ETFs. Ele se concentra no Profitunity System, de Bill Williams, e em como implementar seus indicadores (Fractals, Alligator, AO, AC) em MQL5.

A discussão aqui é sobre práticas de codificação e automação de estratégias, portanto, manter esses pontos será mais útil para os leitores.

Allan Munene Mutiiria
Allan Munene Mutiiria | 2 set. 2025 em 05:57
Miguel Angel Vico Alba #:

O artigo não trata de "colunas" de lucros/perdas ou índices de mercado/ETFs. Ele se concentra no Profitunity System, de Bill Williams, e em como implementar seus indicadores (Fractals, Alligator, AO, AC) em MQL5.

A discussão aqui é sobre práticas de codificação e automação de estratégias, portanto, manter esses pontos será mais útil para os leitores.

Com certeza

Ciência de dados e aprendizado de máquina (Parte 32): Como manter a relevância de modelos de IA com treinamento on-line Ciência de dados e aprendizado de máquina (Parte 32): Como manter a relevância de modelos de IA com treinamento on-line
No mundo em constante transformação do trading, adaptar-se às mudanças do mercado é simplesmente uma necessidade. Todos os dias surgem novos padrões e tendências, o que torna difícil até mesmo para os modelos mais avançados de aprendizado de máquina manterem sua eficácia diante de condições em mutação. Neste artigo, vamos falar sobre como manter os modelos relevantes e capazes de reagir a novos dados de mercado por meio de reeducação automática.
Simulação de mercado: Position View (X) Simulação de mercado: Position View (X)
Precisamos de fato, de algum meio para conseguir lidar com os objetos gráficos que serão criados. A proposta mostrada no artigo anterior, se encaixa perfeitamente bem, em alguns cenários. No entanto, aqui, precisamos de algo um pouco mais elaborado. Isto devido a natureza do problema com que estamos lidando. Assim sendo, não tentaremos de maneira alguma substituir os mecanismos que estão presentes no MetaTrader 5. Isto para conseguir lidar com o ZOrder, além é claro, verificar qual objeto está em primeiro plano ou encoberto por outro objeto. Vamos fazer algo completamente diferente. Aqui vou mostrar quais as modificações que precisam ser feitas no código a fim de conseguir, tirar de alguma forma, proveito do que o MetaTrader 5, já faz para nos.
Desenvolvimento de ferramentas para análise do movimento de preços (Parte 2): Script de comentários analíticos Desenvolvimento de ferramentas para análise do movimento de preços (Parte 2): Script de comentários analíticos
Dando continuidade ao nosso trabalho para simplificar a interação com o comportamento do preço, temos o prazer de apresentar mais uma ferramenta que pode melhorar significativamente sua análise de mercado e ajudar na tomada de decisões bem fundamentadas. Esta ferramenta exibe indicadores técnicos importantes, como os preços do dia anterior, níveis significativos de suporte e resistência, além do volume de negociação, gerando automaticamente dicas visuais no gráfico.
Do básico ao intermediário: Filas, Listas e Árvores (II) Do básico ao intermediário: Filas, Listas e Árvores (II)
Este é um artigo do qual você meu caro leitor, deverá estudar com muita calma. Isto devido ao tipo de coisa que será explicado nele. Apesar de termos procurando manter as coisas o mais simples e didáticas quanto foi possível ser feito. O conteúdo apresentado aqui, é sem sobra de dúvida algo muito complicado para quem está iniciando na programação. Mas isto não é motivo para que você venha a desanimar ou ignorar o que está sendo explicado aqui. Já que este artigo fará um elo, entre dois assuntos completamente diferentes, porém intimamente ligados.