Introdução ao MQL5 (Parte 10): Um Guia para Iniciantes sobre como Trabalhar com Indicadores Embutidos no MQL5
Introdução
Bem-vindo de volta à nossa série sobre MQL5! Estou animado em tê-lo aqui para a parte dez, onde exploraremos outro aspecto crucial do trading algorítmico: trabalhar com indicadores embutidos. Como sempre, esta parte promete ser envolvente e prática, pois continuaremos com nossa abordagem orientada a projeto para garantir que você possa aplicar o que aprender diretamente às suas estratégias de trading.
Estaremos desenvolvendo um EA de Índice de Força Relativa (RSI) neste artigo. Um dos indicadores técnicos mais usados no trading é o RSI. Nós construiremos uma ferramenta que acompanha as condições de mercado e realiza trades automaticamente ao incluir este indicador em nosso EA. Embora o Índice de Força Relativa (RSI) seja o tema deste artigo, as ideias que discutiremos se aplicam à maioria dos indicadores embutidos porque todos eles operam em princípios semelhantes. Como esta série é destinada principalmente a iniciantes, meu objetivo principal será manter as explicações e o código o mais simples possível. Eu sei que, para um novato, é crucial compreender totalmente cada etapa do processo, incluindo por que determinado código é escrito, o que cada componente faz e como as várias partes funcionam juntas.
Código conciso e eficiente em memória é frequentemente muito valorizado no desenvolvimento profissional em MQL5. Embora esse método seja excelente para otimização de eficiência, ele pode ocasionalmente tornar o código mais difícil de entender, particularmente para pessoas não familiarizadas com programação. Por causa disso, adotei deliberadamente uma abordagem mais abrangente e metódica nesta série para garantir que você se sinta à vontade durante todo o processo.
Neste artigo, você aprenderá:
- Como usar indicadores embutidos.
- Usando handles de indicadores para interagir com os indicadores.
- Acessando buffers de indicadores para recuperar valores calculados do indicador.
- Recuperando valores do RSI e seus dados de candlesticks correspondentes do gráfico.
- Identificando máximas e mínimas do RSI para implementar conceitos de varredura de liquidez.
- Criação passo a passo de um EA baseado em valores de RSI e dados de candlestick.
- Criando objetos para marcar diretamente no gráfico máximas e mínimas significativas do RSI para melhor análise
- Definindo um risco percentual por trade para lidar com tamanhos inconsistentes de candles ao usar indicadores embutidos.
Ao final do artigo, você terá uma compreensão abrangente da integração de indicadores embutidos em suas estratégias de trading, com foco em gestão de risco, modificação de risco e percepções práticas para criar e refinar um EA baseado em RSI.
1. Compreendendo Indicadores Embutidos no MQL5
1.1. O que são Indicadores Embutidos?
Os indicadores embutidos do MetaTrader 5 são atalhos úteis para análise de mercado. Eles fornecem informações imediatas sobre as condições do mercado, momentum e movimentos de preço. Por exemplo, as Bandas de Bollinger indicam a quantidade de movimento do mercado, as Médias Móveis ajudam a identificar tendências, e o RSI pode indicar quando um mercado está sobrecomprado ou sobrevendido. Essas ferramentas simplificam muito o trading e economizam seu tempo.
1.2. Handles de Indicadores
No MQL5, handles de indicadores são identificadores únicos atribuídos aos indicadores quando eles são criados ou inicializados. Esses handles atuam como referências aos indicadores, permitindo que você interaja com eles e acesse seus dados. Quando você adiciona um indicador a um gráfico, é necessário inserir propriedades específicas, como o período, tipo de preço e outras configurações que definem como o indicador se comporta.

No código, o handle desempenha um papel semelhante: ele permite que seu programa “saiba” com qual indicador está trabalhando e acesse suas propriedades. Essencialmente, o handle do indicador atua como uma forma de inserir e acionar as configurações do indicador dentro do seu programa, permitindo que você trabalhe com ele de forma eficaz em suas estratégias de trading.
Uma vez que um handle de indicador é criado usando funções como iRSI ou iBands, ele “vincula” seu código a esse indicador específico, para que você possa recuperar e manipular seus dados facilmente. Sem o handle, seu programa não conseguiria distinguir entre diferentes indicadores, nem acessar os valores calculados dos buffers do indicador. Por exemplo, se você quiser inserir as configurações de um indicador em seu código, você usará uma função como iRSI para especificar os parâmetros necessários (como período, preço aplicado e deslocamento), que então criará o handle para o indicador RSI.
Sintaxe:
iRSI(symbol, period, rsi_period, applied_price); Explicação:
- symbol: Este parâmetro especifica o símbolo (par de moedas, ação ou ativo) para o qual o RSI será calculado.
- period: Este é o período (ou timeframe) no qual o RSI será calculado. Ele define até onde o RSI considerará pontos de dados passados.
- rsi_period: Este é o número de períodos usados no cálculo do RSI. O RSI é normalmente calculado usando 14 períodos, mas isso pode ser ajustado para se adequar à sua estratégia.
- applied_price: Este parâmetro define qual tipo de preço usar ao calcular o RSI. O RSI pode ser baseado em diferentes valores de preço, como o preço de fechamento, de abertura ou preços máximo/mínimo.
int rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,14,PRICE_CLOSE);
Explicação:
int rsi_handle:
- Declara uma variável inteira rsi_handle para armazenar o handle do indicador RSI (um ID único para o indicador).
iRSI(...):
- A função usada para calcular o Índice de Força Relativa (RSI) para o símbolo, timeframe e configurações fornecidos.
_Symbol:
- Refere-se ao símbolo de trading atual (por exemplo, EUR/USD) no gráfico. Ele usa automaticamente o símbolo com o qual você está trabalhando.
PERIOD_CURRENT:
- Refere-se ao timeframe do gráfico (por exemplo, 1 hora, 1 dia). Ele garante que o RSI seja calculado com base no timeframe ativo do gráfico.
14:
- O período do RSI, especifica o número de barras/candles a serem usados no cálculo (comumente 14 períodos).
PRICE_CLOSE:
- Especifica que o RSI será calculado usando os preços de fechamento de cada barra ou candle.
É assim que você insere detalhes do indicador diretamente em seu código, de forma semelhante a como você os configuraria em um gráfico do MetaTrader 5. Ao usar funções como iRSI, você define o símbolo, timeframe, período e tipo de preço, assim como faria ao aplicar um indicador no gráfico. Isso permite que seu código acesse e trabalhe com os dados do indicador, garantindo que sua estratégia de trading funcione conforme o esperado com as configurações especificadas.
Os mesmos princípios podem ser aplicados a outros indicadores embutidos no MetaTrader 5. Por exemplo, você pode usar diferentes funções para lidar com vários indicadores, cada um exigindo parâmetros específicos. Aqui estão cinco funções comumente usadas:
- iBands - Para Bandas de Bollinger, permite definir o símbolo, timeframe, período, desvio e preço aplicado.
- iMA - Para Média Móvel, especifica o símbolo, timeframe, período, deslocamento, método e preço aplicado.
- iMACD - Para MACD, define o símbolo, timeframe, EMAs rápida e lenta, período do sinal e preço aplicado.
- iADX - Para Índice Direcional Médio (ADX), especifica o símbolo, timeframe e período.
Com uma variedade de ferramentas de análise técnica para melhorar suas técnicas de trading, o MQL5 oferece muitas outras funções de indicadores embutidos. Aplicar as mesmas ideias a outros indicadores é simples se você souber trabalhar com um. Os indicadores que melhor se encaixam em suas necessidades de trading podem ser encontrados explorando mais a fundo a documentação do MQL5.
1.2. Buffer de Indicador
Após definir um indicador usando um handle, o próximo passo é recuperar seus dados. Isso é feito através dos buffers de indicador, que armazenam os valores calculados de um indicador para cada ponto de preço no gráfico. Cada indicador tem um número específico de buffers dependendo do tipo de dado que ele gera:
Média Móvel (MA)
Este indicador possui 1 buffer, que armazena os valores calculados da média móvel em cada candle.

Índice de Força Relativa (RSI)
Da mesma forma, o indicador RSI possui 1 buffer para armazenar valores do RSI.

Bandas de Bollinger
Este indicador usa 3 buffers para armazenar dados.

- A banda do meio (índice 0) é a linha principal de tendência.
- A banda superior (índice 1) representa um nível potencial de sobrecompra.
- A banda inferior (índice 2) representa um nível potencial de sobrevenda.
Esses buffers podem ser acessados programaticamente usando a função CopyBuffer().
A função CopyBuffer() no MQL5 é usada para recuperar dados do buffer de um indicador para um array para análise ou tomada de decisão posterior. Uma vez que você cria um handle de indicador usando funções como iRSI ou iBands, você usa o CopyBuffer() para acessar os valores calculados do indicador.
Sintaxe:int CopyBuffer(indicator_handle, buffer_number, start_position, count, buffer);
Parâmetros:
- indicator_handle: O identificador único (handle) do indicador criado anteriormente, como a partir de iRSI ou iBands.
- buffer_number: O índice do buffer do qual recuperar dados. Por exemplo, para Bandas de Bollinger, 0 para a banda do meio, 1 para a banda superior, 2 para a banda inferior. Para RSI ou Média Móvel, apenas 0, já que eles possuem um buffer.
- start_pos: A posição inicial no gráfico (0 = candle mais recente).
- count: O número de valores que você deseja recuperar do buffer.
- buffer[]: O array onde os dados do buffer do indicador serão armazenados.
int band_handle; // Bollinger Bands handle double upper_band[], mid_band[], lower_band[]; // Buffers for the bands void OnStart() { // Create the Bollinger Bands indicator band_handle = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE); // Ensure arrays are series for correct indexing ArraySetAsSeries(upper_band, true); ArraySetAsSeries(mid_band, true); ArraySetAsSeries(lower_band, true); // Copy data from buffers (Index 0 = Middle, 1 = Upper, 2 = Lower) CopyBuffer(band_handle, 0, 0, 10, mid_band); // Middle band data CopyBuffer(band_handle, 1, 0, 10, upper_band); // Upper band data CopyBuffer(band_handle, 2, 0, 10, lower_band); // Lower band data // Print the most recent values Print("Middle Band: ", mid_band[0]); Print("Upper Band: ", upper_band[0]); Print("Lower Band: ", lower_band[0]); }
Explicação:
Neste exemplo, demonstramos como usar a função CopyBuffer() para recuperar dados do indicador Bandas de Bollinger. Primeiro, a função iBands cria um handle de indicador usando parâmetros específicos como o símbolo (_Symbol), timeframe (PERIOD_CURRENT), período (20), deslocamento (0), desvio (2.0) e preço aplicado (PRICE_CLOSE). Esse handle, que é armazenado em band_handle, serve como um canal entre nossa aplicação e o indicador Bandas de Bollinger, fornecendo-nos acesso aos números que são calculados. Os dados das bandas superior, média e inferior são então armazenados em três arrays que declaramos: upper_band, mid_band e lower_band. O valor mais recente estará no índice 0 se esses arrays forem configurados como séries usando ArraySetAsSeries. Configurar esses arrays como séries usando ArraySetAsSeries garante que o valor mais recente esteja no índice 0.
Cada um dos três buffers do indicador Bandas de Bollinger — buffer 0 para a banda do meio, buffer 1 para a banda superior e buffer 2 para a banda inferior — tem sua chamada para a função CopyBuffer(). Cada chamada armazena os últimos onze valores no array correspondente após recuperá-los do buffer relacionado. Por fim, o programa imprime os valores mais recentes das bandas média, superior e inferior usando Print(). A relação entre o preço e as Bandas de Bollinger pode então ser utilizada para identificar possíveis rompimentos ou reversões de tendência, entre outros usos desses dados para análise adicional ou tomada de decisão dentro do sistema de trading.
2. Desenvolvendo o Expert Advisor (EA) baseado em RSI
2.1. Como o EA Funciona:
O EA considera os níveis de sobrecompra e sobrevenda como marcadores cruciais de uma possível reversão.
2.1.1. Lógica para Compra
- Verifique se o valor do RSI está abaixo de 30.
- Determina se o RSI formou uma mínima.
- O EA identifica a mínima correspondente do candle no gráfico normal.
- Espera o preço varrer a liquidez rompendo abaixo da mínima do candle designado.
- Quando o primeiro candle de alta fecha acima da mínima após o preço romper a mínima, o EA inicia uma operação de compra antecipando uma subida.

2.1.2. Lógica para Venda
- Verifique se o valor do RSI está acima de 70.
- Determina se o RSI formou uma máxima.
- O EA identifica a máxima do candle correspondente no gráfico normal.
- Em seguida, ele monitora a ação do preço, aguardando o preço romper acima da máxima do candle identificado, varrendo a liquidez.
- Após o preço romper a máxima, o EA coloca uma operação de venda quando o primeiro candle de baixa fecha abaixo da máxima, antecipando um movimento de queda.

2.2. Adicionando a Biblioteca de Trade
O primeiro passo ao construir um Expert Advisor (EA) que abre, fecha ou modifica posições é incluir a trade library. Essa biblioteca fornece funções essenciais para executar e gerenciar operações programaticamente.
Exemplo:#include <Trade/Trade.mqh> // Include the trade library for trading functions // Create an instance of the CTrade class for trading operations CTrade trade; //magic number input int MagicNumber = 1111; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Set the magic number for the EA's trades trade.SetExpertMagicNumber(MagicNumber); // Return initialization success return(INIT_SUCCEEDED); }
Explicação:
Incluindo a Biblioteca de Trade
- Para que você use a classe CTrade, a trade library deve ser importada usando a declaração #include <Trade/Trade.mqh>. Essa classe simplifica processos de trading ao oferecer recursos para gerenciar take-profit, stop-loss, ordens a mercado, ordens pendentes e outras tarefas relacionadas a trading.
Criando uma instância de CTrade
- A linha CTrade trade; cria uma instância da classe CTrade, que será usada ao longo do EA para executar e gerenciar operações.
- O MagicNumber é declarado como uma variável de entrada usando a palavra-chave Input. Isso permite que o usuário do EA defina um identificador exclusivo para as operações feitas pelo EA.
Por que isso é importante?
- O magic number garante que as operações colocadas pelo EA possam ser identificadas e gerenciadas separadamente de outras operações.
- Ele permite que o EA distinga suas operações das manuais ou de outros EAs.
- Ao modificar o magic number, o usuário pode executar o EA em diferentes ativos ou até no mesmo ativo sob condições de mercado ou timeframes variados.
int OnInit() { // Set the magic number for the EA's trades trade.SetExpertMagicNumber(MagicNumber); // Return initialization success return(INIT_SUCCEEDED); }
Funções de Inicialização:
A função OnInit() é executada quando o EA inicia. Aqui, o método SetExpertMagicNumber() atribui o magic number especificado ao EA, garantindo que todas as operações abertas por esta instância do EA carreguem esse identificador. Essa abordagem fornece flexibilidade, pois os usuários podem ajustar o magic number para diferentes cenários de trading, facilitando o gerenciamento de múltiplas instâncias do EA em vários instrumentos ou estratégias.
2.3. Recuperando valores de RSI e dados de candlestick
Recuperar dados de mercado precisos é fundamental para que o EA realize operações de trading eficazes. A lógica deste EA gira em torno de entender padrões de candlestick e o indicador RSI, tornando crucial obter esses valores de maneira confiável. Esses dados permitem que o EA analise se máximas e mínimas do RSI correspondem a candlesticks específicos, o que é chave para determinar possíveis pontos de entrada e saída. Ao combinar valores de RSI com dados de candlestick, o EA garante que avalie as condições de mercado de forma holística, identificando condições de sobrecompra ou sobrevenda em contexto com a ação do preço. Essa abordagem fortalece a capacidade do EA de tomar decisões de trading lógicas e precisas.
2.3.1. Recuperando valores de RSI
Conforme discutido anteriormente, obter valores de RSI é um passo essencial para o EA avaliar as condições de mercado. Isso é realizado em duas partes: configurar as propriedades do indicador RSI usando o handle iRSI e recuperar os valores reais de RSI por meio da função CopyBuffer.
Exemplo:
#include <Trade/Trade.mqh> CTrade trade; // Magic number input int MagicNumber = 1111; //RSI handle int rsi_handle; double rsi_buffer[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Configure RSI buffer as a series for easier indexing ArraySetAsSeries(rsi_buffer, true); // Initialize RSI handle for the current symbol, timeframe, and parameters rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE); // Set the magic number (for trading, not relevant to RSI retrieval here) trade.SetExpertMagicNumber(MagicNumber); return (INIT_SUCCEEDED); // Indicate successful initialization } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Copy RSI values from the indicator into the buffer CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer); // Display the most recent RSI value for verification Comment("rsi_buffer[0]: ", rsi_buffer[0]); }
Explicação:
Arrumando o Buffer do RSI
- Na função OnInit, a função ArraySetAsSeries é usada para configurar o rsi_buffer como um array de série, significando que os dados mais recentes estarão no índice 0. Isso simplifica o acesso aos valores mais recentes do RSI.
Criando o handle do RSI
- Dentro da função OnTick, a função iRSI é chamada para inicializar o handle do RSI (rsi_handle). Essa função configura as propriedades do indicador RSI, como o símbolo (_Symbol), timeframe (PERIOD_CURRENT), período (14) e preço aplicado (PRICE_CLOSE).
Parâmetros:
- _Symbol: O instrumento de trading para o qual o RSI é calculado.
- PERIOD_CURRENT: O timeframe atual do gráfico.
- 14: O período do RSI (um valor padrão comumente usado).
- PRICE_CLOSE: O RSI é calculado com base nos preços de fechamento.
Copiando valores de RSI
- A função CopyBuffer busca os valores do RSI para o indicador e preenche o array rsi_buffer. Ela começa a copiar a partir do valor mais recente (deslocamento 1) e recupera até 100 valores.
Parâmetros:
- 0: Especifica o índice do buffer do indicador RSI (0 para a linha principal).
- 1: Começa a copiar a partir do segundo valor mais recente do RSI, já que o RSI mais recente ainda está se formando e seus valores são instáveis.
- 100: Recupera até 100 valores.
Essa abordagem garante que o EA tenha acesso a valores atualizados de RSI, os quais são críticos para analisar condições de sobrecompra e sobrevenda e direcionar decisões de trading.
Exibindo o valor do RSI
- O código valida o procedimento de recuperação de dados exibindo o valor mais recente do RSI (rsi_buffer[0]) no gráfico usando a função Comment.
- Essa abordagem garante que o EA tenha acesso a valores atualizados de RSI, os quais são críticos para analisar condições de sobrecompra e sobrevenda e direcionar decisões de trading.
Saída:

2.3.2. Recuperando dados de Candlestick
Para analisar a atividade de preço e combiná-la com as leituras do RSI, é imperativo recuperar dados de candlestick. Para recuperar informações importantes de candlestick, como os preços de abertura, fechamento, máxima e mínima, juntamente com os timestamps que os correlacionam, o EA emprega funções específicas. Para avaliar as condições de mercado e tomar decisões de trading sensatas, esses pontos de dados são essenciais.
Exemplo:
#include <Trade/Trade.mqh> CTrade trade; // Magic number input int MagicNumber = 1111; int rsi_handle; double rsi_buffer[]; double open[]; double close[]; double high[]; double low[]; datetime time[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Configure RSI buffer as a series for easier indexing ArraySetAsSeries(rsi_buffer, true); // Initialize RSI handle for the current symbol, timeframe, and parameters rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE); // Configure candlestick arrays as series ArraySetAsSeries(open, true); ArraySetAsSeries(close, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(time, true); // Set the magic number trade.SetExpertMagicNumber(MagicNumber); return (INIT_SUCCEEDED); // Indicate successful initialization } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Copy RSI values from the indicator into the buffer CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer); // Copy candlestick data CopyOpen(_Symbol, PERIOD_CURRENT, 1, 100, open); CopyClose(_Symbol, PERIOD_CURRENT, 1, 100, close); CopyHigh(_Symbol, PERIOD_CURRENT, 1, 100, high); CopyLow(_Symbol, PERIOD_CURRENT, 1, 100, low); CopyTime(_Symbol, PERIOD_CURRENT, 1, 100, time); // Display the most recent candlestick data for verification Comment("open[0]: ", open[0], "\nclose[0]: ", close[0], "\nhigh[0]: ", high[0], "\nlow[0]: ", low[0], "\ntime[0]: ", time[0]); }
Explicação:
Arrumando os Arrays de Candlestick
- Na função OnInit, a função ArraySetAsSeries é usada para configurar os arrays open, close, high, low e time como séries.
- Isso garante que os dados mais recentes apareçam no índice 0, simplificando a recuperação e análise dos dados do candlestick mais recentemente concluído.
Copiando dados de Candlestick
- Na função OnTick, as funções CopyOpen, CopyClose, CopyHigh, CopyLow e CopyTime são usadas para recuperar os respectivos dados dos candlesticks.
Parâmetros:
- _Symbol: O instrumento de trading atual.
- PERIOD_CURRENT: O timeframe atual do gráfico (por exemplo, M1, H1).
- 1: Começa a copiar a partir do segundo candle mais recente porque o candle mais recente (índice 0) ainda está se formando e seus valores são instáveis.
- 100: Recupera até 100 valores de candlestick para análise.
Cada função preenche seu respectivo array:
- open[]: Contém os preços de abertura dos candlesticks especificados.
- close[]: Contém os preços de fechamento.
- high[]: Contém os preços máximos.
- low[]: Contém os preços mínimos.
- time[]: Armazena os timestamps de abertura para sincronização com a ação do preço.
Exibindo dados de Candlestick
A função Comment é usada para exibir no gráfico, para fins de validação, os dados do candlestick mais recentemente concluído (índice 0):
- open[0]: O preço de abertura.
- close[0]: O preço de fechamento.
- high[0]: O preço máximo.
- low[0]: O preço mínimo.
- time[0]: O timestamp indicando quando o candle abriu.
2.3.3. Determinando máximas e mínimas do RSI
O EA deve reconhecer mínimas do RSI quando o RSI cai abaixo dos níveis de sobrevenda (30) e máximas do RSI quando ele sobe acima dos níveis de sobrecompra (70). Para identificar possíveis áreas de interesse, esses pontos do RSI são então conectados a candlesticks particulares no gráfico. Esta etapa é a base do funcionamento do EA, pois esses marcadores são essenciais para configurar a lógica de varreduras de liquidez.
Determinar máximas e mínimas do RSI é bastante simples:
Mínima do RSI: 
Máxima do RSI:

Exemplo:
// Magic number input int MagicNumber = 1111; // RSI handle and buffer int rsi_handle; double rsi_buffer[]; // Candlestick data arrays double open[]; double close[]; double high[]; double low[]; datetime time[]; // Variables to store high and low levels double max_high = 0; // Maximum high for the candlesticks datetime min_time1 = 0; // Time of the maximum high candlestick double min_low = 0; // Minimum low for the candlesticks datetime min_time2 = 0; // Time of the minimum low candlestick // Variables to store RSI highs and lows datetime time_low = 0; // Time of the RSI low datetime times_high = 0; // Time of the RSI high //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Configure RSI buffer as a series for easier indexing ArraySetAsSeries(rsi_buffer, true); // Initialize RSI handle for the current symbol, timeframe, and parameters rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE); // Configure candlestick arrays as series ArraySetAsSeries(open, true); ArraySetAsSeries(close, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(time, true); // Set the magic number for the EA trade.SetExpertMagicNumber(MagicNumber); return (INIT_SUCCEEDED); // Indicate successful initialization } //+------------------------------------------------------------------+ //| Expert tick function | | //+------------------------------------------------------------------+ void OnTick() { // Copy RSI values from the indicator into the buffer CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer); // Copy candlestick data (open, close, high, low, time) CopyOpen(_Symbol, PERIOD_CURRENT, 1, 100, open); CopyClose(_Symbol, PERIOD_CURRENT, 1, 100, close); CopyHigh(_Symbol, PERIOD_CURRENT, 1, 100, high); CopyLow(_Symbol, PERIOD_CURRENT, 1, 100, low); CopyTime(_Symbol, PERIOD_CURRENT, 1, 100, time); // Loop to find the maximum high from a bullish candlestick pattern for(int i = 0; i < 12; i++) { // Check for a bullish pattern: current close < open and previous close > open if(close[i] < open[i] && close[i+1] > open[i+1]) { // Calculate the maximum high between the two candlesticks max_high = MathMax(high[i], high[i+1]); // Record the time of the corresponding candlestick min_time1 = MathMin(time[i], time[i+1]); break; } } // Loop to find the minimum low from a bearish candlestick pattern for(int i = 0; i < 12; i++) { // Check for a bearish pattern: current close > open and previous close < open if(close[i] > open[i] && close[i+1] < open[i+1]) { // Calculate the minimum low between the two candlesticks min_low = MathMin(low[i], low[i+1]); // Record the time of the corresponding candlestick min_time2 = MathMin(time[i], time[i+1]); break; } } // Loop to find the RSI low point for(int i = 0; i < 12; i++) { // Check if the RSI is oversold and forms a low point if(rsi_buffer[i+1] < 30 && rsi_buffer[i] > rsi_buffer[i+1]) { // Record the time of the RSI low time_low = time[i+1]; break; } } // Loop to find the RSI high point for(int i = 0; i < 12; i++) { // Check if the RSI is overbought and forms a high point if(rsi_buffer[i+1] > 70 && rsi_buffer[i] < rsi_buffer[i+1]) { // Record the time of the RSI high times_high = time[i+1]; break; } } }
Explicação:
Mínimas do RSI (Condições de sobrevenda)
- O loop procura pontos onde o RSI está abaixo de 30 (rsi_buffer[i+1] < 30) e começa a subir (rsi_buffer[i] > rsi_buffer[i+1]).
- Essas condições indicam que o RSI atingiu uma mínima e está invertendo a direção.
- O timestamp do candlestick correspondente é armazenado em time_low.
Máximas do RSI (Condições de sobrecompra)
- O loop procura pontos onde o RSI está acima de 70 (rsi_buffer[i+1] > 70) e começa a cair (rsi_buffer[i] < rsi_buffer[i+1]).
- Essas condições indicam que o RSI atingiu uma máxima e está invertendo a direção.
- O timestamp do candlestick correspondente é armazenado em times_high.
2.3.4. Marcando a mínima e a máxima no gráfico
2.3.4.1. Marcando a mínima no gráfico
Uma vez satisfeita a lógica para identificar a mínima do RSI, o programa avalia os dados de candlestick para localizar a mínima correspondente no gráfico. Como são necessários dois valores de RSI para formar uma mínima, o EA usa as mínimas dos dois candlesticks correspondentes para determinar o nível preciso.
Essa mínima é crucial, pois estabelece a área onde o programa aguardará uma potencial varredura de liquidez.
Lógica:
- O RSI forma uma mínima quando:
- rsi_buffer[i+1] < 30: O RSI mergulha abaixo do nível de sobrevenda.
- rsi_buffer[i] > rsi_buffer[i+1]: O RSI começa a subir após atingir seu ponto mais baixo.
- Uma vez confirmada a mínima do RSI, o programa identifica a mínima mínima usando o array low dos dois candlesticks correspondentes no gráfico.
- Essa mínima mínima define o nível onde o EA aguarda uma potencial varredura de liquidez.
- Assim que a mínima do RSI é identificada, o programa recupera os preços mínimos dos dois candlesticks correspondentes a partir do array low.
- Usando a função MathMin(), ele calcula o menor valor entre essas duas mínimas, marcando o nível para monitorar varreduras de liquidez.
- Essa mínima serve como o ponto onde o EA espera uma possível reversão ou varredura de liquidez, um fator crítico nas decisões de trading.
// Loop to find RSI and candlestick lows for(int i = 0; i < 12; i++) { // Check if the RSI is oversold and forms a low point if(rsi_buffer[i+1] < 30 && rsi_buffer[i] > rsi_buffer[i+1]) { // Record the time of the RSI low time_low = time[i+1]; // Find the minimum low from the two corresponding candlesticks min_low = (double)MathMin(low[i], low[i+1]); // Break the loop once the low is found break; } }

2.3.4.2.Marcando a máxima no gráfico
Quando a lógica para identificar a máxima do RSI é satisfeita, o programa avalia os dados de candlestick correspondentes para determinar a máxima no gráfico. Como são necessários dois valores de RSI para formar uma máxima, o EA usa as máximas dos dois candlesticks correspondentes para determinar o nível exato.
Essa máxima se torna a referência para definir a área onde o EA aguardará uma potencial varredura de liquidez. A abordagem é o inverso da lógica para mínimas.
Lógica:
- O RSI forma uma máxima quando:
- rsi_buffer[i+1] > 70: O RSI sobe acima do nível de sobrecompra.
- rsi_buffer[i] < rsi_buffer[i+1]: O RSI começa a cair após atingir seu ponto mais alto.
- Uma vez confirmada a máxima do RSI, o programa identifica a máxima máxima usando o array high dos dois candlesticks correspondentes no gráfico.
- Essa máxima máxima define o nível onde o EA aguarda uma potencial varredura de liquidez.
- Assim que a máxima do RSI é identificada, o programa recupera os preços máximos dos dois candlesticks correspondentes a partir do array high.
- Usando a função MathMax(), ele calcula o valor mais alto entre essas duas máximas, marcando o nível para monitorar varreduras de liquidez.
// Loop to find RSI and candlestick highs for(int i = 0; i < 12; i++) { // Check if the RSI is overbought and forms a high point if(rsi_buffer[i+1] > 70 && rsi_buffer[i] < rsi_buffer[i+1]) { // Record the time of the RSI high times_high = time[i+1]; // Find the maximum high from the two corresponding candlesticks max_high = (double)MathMax(high[i], high[i+1]); // Break the loop once the high is found break; } }

2.3.5. Controlando Máximas e Mínimas do RSI com Atraso de 12 Candles
O potencial para que várias máximas ou mínimas do RSI ocorram em um curto período de tempo é uma das dificuldades ao utilizar o RSI para identificar máximas e mínimas, particularmente quando o RSI permanece nas zonas de sobrecompra ou sobrevenda. Como resultado, diferentes níveis podem ser designados para varreduras de liquidez. A lógica adiciona uma espera de 12 candles para resolver isso, garantindo que, após uma máxima ou mínima ser determinada, ela não seja atualizada até que 12 candles tenham se formado.
Exemplo:
// Magic number input int MagicNumber = 1111; int rsi_handle; double rsi_buffer[]; double open[]; double close[]; double high[]; double low[]; datetime time[]; double max_high = 0; datetime min_time1 = 0; double min_low = 0; datetime min_time2 = 0; datetime time_low = 0; datetime times_high = 0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Configure RSI buffer as a series for easier indexing ArraySetAsSeries(rsi_buffer, true); // Initialize RSI handle for the current symbol, timeframe, and parameters rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE); // Configure candlestick arrays as series ArraySetAsSeries(open, true); ArraySetAsSeries(close, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(time, true); // Set the magic number trade.SetExpertMagicNumber(MagicNumber); return (INIT_SUCCEEDED); // Indicate successful initialization } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Copy RSI values from the indicator into the buffer CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer); // Copy candlestick data CopyOpen(_Symbol, PERIOD_CURRENT, 1, 100, open); CopyClose(_Symbol, PERIOD_CURRENT, 1, 100, close); CopyHigh(_Symbol, PERIOD_CURRENT, 1, 100, high); CopyLow(_Symbol, PERIOD_CURRENT, 1, 100, low); CopyTime(_Symbol, PERIOD_CURRENT, 1, 100, time); static double max_high_static = max_high; static datetime min_time1_static = min_time1; static double min_low_static = min_low; static datetime min_time2_static = min_time2; int total_bar_high = Bars(_Symbol,PERIOD_CURRENT,min_time1_static,TimeCurrent()); for(int i = 0; i < 12; i++) { if(close[i] < open[i] && close[i+1] > open[i+1]) { max_high = (double)MathMax(high[i],high[i+1]); min_time1 = (datetime)MathMin(time[i],time[i+1]); break; } } int total_bar_low = Bars(_Symbol,PERIOD_CURRENT,min_time2_static,TimeCurrent()); for(int i = 0; i < 12; i++) { if(close[i] > open[i] && close[i+1] < open[i+1]) { min_low = (double)MathMin(low[i],low[i+1]); min_time2 = (datetime)MathMin(time[i],time[i+1]); break; } } for(int i = 0; i < 12; i++) { if(rsi_buffer[i+1] < 30 && rsi_buffer[i] > rsi_buffer[i+1]) { time_low = time[i+1]; break; } } for(int i = 0; i < 12; i++) { if(rsi_buffer[i+1] > 70 && rsi_buffer[i] < rsi_buffer[i+1]) { times_high = time[i+1]; break; } } if((total_bar_high == 0 || total_bar_high > 12) && (min_time1 == times_high)) { max_high_static = max_high; min_time1_static = min_time1; } else if(min_time1 != times_high && total_bar_high > 13) { max_high_static = 0; min_time1_static = 0; } if((total_bar_low == 0 || total_bar_low > 12) && (min_time2 == time_low)) { min_low_static = min_low; min_time2_static = min_time2; } else if(min_time2 != time_low && total_bar_low > 13) { min_low_static = 0; min_time2_static = 0; } }
Explicação:
Identificação de Máximas/Mínimas do RSI e de Candlestick:
- A atividade do indicador em relação aos níveis de sobrecompra ou sobrevenda é usada pela lógica do RSI para avaliar se uma máxima ou mínima está se formando.
- Se for o caso, o programa determina a máxima mais alta ou a mínima mais baixa a partir dos candlesticks associados.
- Usando Bars(), o programa calcula quantos candles se formaram desde a última máxima ou mínima.
- Os valores de máxima ou mínima são atualizados apenas se ao menos 12 candles tiverem decorrido, evitando mudanças frequentes.
- Se mais de 13 candles se formarem e a condição do RSI para a máxima ou mínima anterior não for mais válida, os valores armazenados são limpos.
- Isso garante que o EA não dependa de informações desatualizadas para suas decisões de trading.
Nesta seção, aprimoramos o EA para representar visualmente as máximas e mínimas do RSI identificadas, traçando linhas diretamente no gráfico. Isso não apenas permite que o EA use esses objetos de linha para identificar programaticamente as máximas e mínimas, como também possibilita que traders monitorem manualmente os níveis críticos para uma melhor tomada de decisão.
#include <Trade/Trade.mqh> CTrade trade; // Magic number input int MagicNumber = 1111; int rsi_handle; double rsi_buffer[]; double open[]; double close[]; double high[]; double low[]; datetime time[]; double max_high = 0; datetime min_time1 = 0; double min_low = 0; datetime min_time2 = 0; datetime time_low = 0; datetime times_high = 0; string high_obj_name = "High_Line"; string low_obj_name = "Low_Line"; long chart_id; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Configure RSI buffer as a series for easier indexing ArraySetAsSeries(rsi_buffer, true); // Initialize RSI handle for the current symbol, timeframe, and parameters rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE); // Configure candlestick arrays as series ArraySetAsSeries(open, true); ArraySetAsSeries(close, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(time, true); // Set the magic number trade.SetExpertMagicNumber(MagicNumber); return (INIT_SUCCEEDED); // Indicate successful initialization } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Copy RSI values from the indicator into the buffer CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer); // Copy candlestick data CopyOpen(_Symbol, PERIOD_CURRENT, 1, 100, open); CopyClose(_Symbol, PERIOD_CURRENT, 1, 100, close); CopyHigh(_Symbol, PERIOD_CURRENT, 1, 100, high); CopyLow(_Symbol, PERIOD_CURRENT, 1, 100, low); CopyTime(_Symbol, PERIOD_CURRENT, 1, 100, time); static double max_high_static = max_high; static datetime min_time1_static = min_time1; static double min_low_static = min_low; static datetime min_time2_static = min_time2; //CHART ID chart_id = ChartID(); int total_bar_high = Bars(_Symbol,PERIOD_CURRENT,min_time1_static,TimeCurrent()); for(int i = 0; i < 12; i++) { if(close[i] < open[i] && close[i+1] > open[i+1]) { max_high = (double)MathMax(high[i],high[i+1]); min_time1 = (datetime)MathMin(time[i],time[i+1]); break; } } int total_bar_low = Bars(_Symbol,PERIOD_CURRENT,min_time2_static,TimeCurrent()); for(int i = 0; i < 12; i++) { if(close[i] > open[i] && close[i+1] < open[i+1]) { min_low = (double)MathMin(low[i],low[i+1]); min_time2 = (datetime)MathMin(time[i],time[i+1]); break; } } for(int i = 0; i < 12; i++) { if(rsi_buffer[i+1] < 30 && rsi_buffer[i] > rsi_buffer[i+1]) { time_low = time[i+1]; break; } } for(int i = 0; i < 12; i++) { if(rsi_buffer[i+1] > 70 && rsi_buffer[i] < rsi_buffer[i+1]) { times_high = time[i+1]; break; } } if((total_bar_high == 0 || total_bar_high > 12) && (min_time1 == times_high)) { max_high_static = max_high; min_time1_static = min_time1; } else if(min_time1 != times_high && total_bar_high > 13) { max_high_static = 0; min_time1_static = 0; } if((total_bar_low == 0 || total_bar_low > 12) && (min_time2 == time_low)) { min_low_static = min_low; min_time2_static = min_time2; } else if(min_time2 != time_low && total_bar_low > 13) { min_low_static = 0; min_time2_static = 0; } ObjectCreate(ChartID(),high_obj_name,OBJ_TREND,0,min_time1_static,max_high_static,TimeCurrent(),max_high_static); ObjectSetInteger(chart_id,high_obj_name,OBJPROP_COLOR,clrGreen); ObjectSetInteger(chart_id,high_obj_name,OBJPROP_WIDTH,3); ObjectCreate(ChartID(),low_obj_name,OBJ_TREND,0,min_time2_static,min_low_static,TimeCurrent(),min_low_static); ObjectSetInteger(chart_id,low_obj_name,OBJPROP_COLOR,clrRed); ObjectSetInteger(chart_id,low_obj_name,OBJPROP_WIDTH,3); }
Explicação:
O código cria duas linhas de tendência no gráfico usando a função ObjectCreate(): high_obj_name, que marca a máxima, e low_obj_name, que marca a mínima. Essas linhas de tendência se estendem até o momento atual (TimeCurrent()) e são derivadas dos níveis de preço de máxima e mínima calculados (max_high_static e min_low_static) para os tempos correspondentes (min_time1_static e min_time2_static). Isso permite que o trader monitore visualmente os pontos de máxima e mínima do gráfico.
Você pode alterar a aparência dessas linhas de tendência usando a função ObjectSetInteger(). Ela é definida como verde para a linha de tendência da máxima e vermelha para a linha de tendência da mínima. Para que ambas as linhas sejam facilmente legíveis no gráfico, sua largura é definida como 3. Com essa ferramenta visual, tanto o trader quanto o EA podem manter mais facilmente o olho em níveis de preço-chave e avaliar possíveis movimentos de mercado, como varreduras de liquidez.
2.3.7. Especificando Condições de Compra e Venda para Varreduras de Liquidez
As condições de trading devem garantir precisão e aderência a cenários específicos de mercado para qualificar uma varredura de liquidez das máximas e mínimas identificadas. Esta seção estabelece a lógica para executar operações de compra e venda em torno desses níveis críticos. — ¶ —————————————————————
// Magic number input int MagicNumber = 1111; int rsi_handle; double rsi_buffer[]; double open[]; double close[]; double high[]; double low[]; datetime time[]; double max_high = 0; datetime min_time1 = 0; double min_low = 0; datetime min_time2 = 0; datetime time_low = 0; datetime times_high = 0; string high_obj_name = "High_Line"; string low_obj_name = "Low_Line"; long chart_id; double take_profit; double ask_price = 0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Configure RSI buffer as a series for easier indexing ArraySetAsSeries(rsi_buffer, true); // Initialize RSI handle for the current symbol, timeframe, and parameters rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE); // Configure candlestick arrays as series ArraySetAsSeries(open, true); ArraySetAsSeries(close, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(time, true); // Set the magic number trade.SetExpertMagicNumber(MagicNumber); return (INIT_SUCCEEDED); // Indicate successful initialization } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Copy RSI values from the indicator into the buffer CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer); // Copy candlestick data CopyOpen(_Symbol, PERIOD_CURRENT, 1, 100, open); CopyClose(_Symbol, PERIOD_CURRENT, 1, 100, close); CopyHigh(_Symbol, PERIOD_CURRENT, 1, 100, high); CopyLow(_Symbol, PERIOD_CURRENT, 1, 100, low); CopyTime(_Symbol, PERIOD_CURRENT, 1, 100, time); static double max_high_static = max_high; static datetime min_time1_static = min_time1; static double min_low_static = min_low; static datetime min_time2_static = min_time2; //GETTING TOTAL POSITIONS int totalPositions = 0; for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(PositionSelectByTicket(ticket)) { if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id)) { totalPositions++; } } } ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK); if(totalPositions < 1) { if(((low[0] < min_low_static && close[0] > min_low_static && close[0] > open[0]) || (low[1] < min_low_static && close[0] > min_low_static && close[0] > open[0]) || (low[2] < min_low_static && close[0] > min_low_static && close[0] > open[0] && close[1] < open[1]))) { take_profit = (close[0] - low[0]) * 3 + close[0]; trade.Buy(0.5,_Symbol,ask_price, low[0], take_profit); } else if(((high[0] > max_high_static && close[0] < max_high_static && close[0] < open[0]) || (high[1] > max_high_static && close[0] < max_high_static && close[0] < open[0]) || (high[2] > max_high_static && close[0] < max_high_static && close[0] < open[0] && close[1] > open[1]))) { take_profit = MathAbs((high[0] - close[0]) * 3 - close[0]); // Adjusted take-profit calculation trade.Sell(0.5,_Symbol,ask_price, high[0], take_profit); } } //CHART ID chart_id = ChartID(); int total_bar_high = Bars(_Symbol,PERIOD_CURRENT,min_time1_static,TimeCurrent()); for(int i = 0; i < 12; i++) { if(close[i] < open[i] && close[i+1] > open[i+1]) { max_high = (double)MathMax(high[i],high[i+1]); min_time1 = (datetime)MathMin(time[i],time[i+1]); break; } } int total_bar_low = Bars(_Symbol,PERIOD_CURRENT,min_time2_static,TimeCurrent()); for(int i = 0; i < 12; i++) { if(close[i] > open[i] && close[i+1] < open[i+1]) { min_low = (double)MathMin(low[i],low[i+1]); min_time2 = (datetime)MathMin(time[i],time[i+1]); break; } } for(int i = 0; i < 12; i++) { if(rsi_buffer[i+1] < 30 && rsi_buffer[i] > rsi_buffer[i+1]) { time_low = time[i+1]; break; } } for(int i = 0; i < 12; i++) { if(rsi_buffer[i+1] > 70 && rsi_buffer[i] < rsi_buffer[i+1]) { times_high = time[i+1]; break; } } if((total_bar_high == 0 || total_bar_high > 12) && (min_time1 == times_high)) { max_high_static = max_high; min_time1_static = min_time1; } else if(min_time1 != times_high && total_bar_high > 13) { max_high_static = 0; min_time1_static = 0; } if((total_bar_low == 0 || total_bar_low > 12) && (min_time2 == time_low)) { min_low_static = min_low; min_time2_static = min_time2; } else if(min_time2 != time_low && total_bar_low > 13) { min_low_static = 0; min_time2_static = 0; } ObjectCreate(ChartID(),high_obj_name,OBJ_TREND,0,min_time1_static,max_high_static,TimeCurrent(),max_high_static); ObjectSetInteger(chart_id,high_obj_name,OBJPROP_COLOR,clrGreen); ObjectSetInteger(chart_id,high_obj_name,OBJPROP_WIDTH,3); ObjectCreate(ChartID(),low_obj_name,OBJ_TREND,0,min_time2_static,min_low_static,TimeCurrent(),min_low_static); ObjectSetInteger(chart_id,low_obj_name,OBJPROP_COLOR,clrRed); ObjectSetInteger(chart_id,low_obj_name,OBJPROP_WIDTH,3); }
Explicação:
Prevenindo Múltiplas Posições com totalPositions
A variável totalPositions garante que o Expert Advisor (EA) mantenha apenas uma posição ativa por vez. Esse mecanismo evita superexposição ao verificar todas as posições abertas e confirmar se o magic number (identificador das operações do EA) e o símbolo correspondem ao gráfico atual. Se não existir posição correspondente (totalPositions < 1), o EA avalia condições para abrir uma nova operação.
Esse mecanismo de segurança garante execução disciplinada e evita sobreposição de posições, tornando a estratégia mais fácil de gerenciar e menos propensa a riscos compostos.
Condições para Operações de Compra (Varredura de Liquidez na Mínima)
A lógica de compra é centrada na detecção de uma varredura de liquidez em um nível de mínima previamente identificado (min_low_static) e na garantia de confirmação altista.
Condições
Rompimento Abaixo da Mínima:
- Qualquer um dos três candles recentes (low[0], low[1] ou low[2]) deve cair abaixo da mínima identificada, sugerindo que a liquidez foi varrida abaixo do nível-chave.
Recuperação Altista:
- O preço de fechamento atual (close[0]) deve se recuperar acima da mínima (min_low_static), sinalizando intenção altista após a varredura.
- Além disso, o candle deve ser altista (close[0] > open[0]), refletindo momentum de alta.
- Se dois candles antes do atual (close[1] < open[1]) foram de baixa, isso adiciona um componente de reversão, reforçando a legitimidade da varredura.
Execução da Operação
Uma vez satisfeitas as condições, uma ordem de compra é executada com:- Take Profit: Três vezes a faixa entre o preço de fechamento e a mínima (take_profit = (close[0] - low[0]) * 3 + close[0]).
- Stop Loss: O nível de mínima identificado (low[0]).
Condições para Operações de Venda (Varredura de Liquidez na Máxima)
A lógica de venda espelha a lógica de compra, mas foca em identificar uma varredura de liquidez em um nível de máxima previamente estabelecido (max_high_static) com confirmação baixista.
Condições
Rompimento Acima da Máxima:
- Qualquer um dos três candles recentes (high[0], high[1] ou high[2]) deve exceder a máxima identificada, indicando uma varredura de liquidez acima desse nível-chave.
Reversão Baixista:
- O preço de fechamento atual (close[0]) deve cair abaixo da máxima (max_high_static), indicando falha em sustentar o rompimento.
- Além disso, o candle deve ser de baixa (close[0] < open[0]), sugerindo momentum de queda.
- Se dois candles antes do atual (close[1] > open[1]) foram de alta, isso destaca uma potencial reversão.
Execução da Operação
Uma vez atendidas as condições, uma ordem de venda é colocada com:
- Take Profit: Três vezes a faixa entre a máxima e o preço de fechamento (take_profit = MathAbs((high[0] - close[0]) * 3 - close[0])).
- Stop Loss: O nível de máxima identificado (high[0]).
Resumo
Ao combinar o mecanismo totalPositions com condições bem definidas de compra e venda, esta estratégia garante precisão e limita o risco nas decisões de trading:
- Operações de compra são acionadas após uma varredura abaixo de um nível de mínima chave com recuperação altista.
- Operações de venda são iniciadas após uma varredura acima de um nível de máxima chave com reversão baixista.
Essa abordagem estruturada alavanca varreduras de liquidez como conceito central, garantindo que as operações sejam executadas apenas sob condições favoráveis. Uma limitação desse método é que o stop-loss é definido dinamicamente com base no low[0] ou high[0] do candle. Isso significa que o risco por operação varia dependendo do tamanho do candlestick, levando a exposição inconsistente. Para resolver isso, a estratégia deve permitir especificar uma porcentagem fixa do saldo da conta a arriscar por operação (por exemplo, 2%). Isso garante gestão de risco consistente ao calcular o tamanho da posição com base na distância entre o preço de entrada e o stop-loss, alinhado à porcentagem de risco especificada.
2.3.8. Gerenciamento de Risco e Modificações de Break-Even
Ao trabalhar com indicadores embutidos, o gerenciamento de risco eficaz é essencial para garantir desempenho consistente. Os tamanhos dos candles podem variar significativamente, tornando crucial especificar a porcentagem do saldo da sua conta que você deseja arriscar por operação. Essa consistência permite que sua relação risco-retorno (RRR) compense perdas durante operações vencedoras. Além disso, modificações de break-even garantem que os lucros sejam protegidos quando as operações se movem a favor da estratégia. Incorporar modificações de break-even protege lucros quando as operações se movem a seu favor, aumentando a robustez geral da estratégia.
Exemplo:
//+------------------------------------------------------------------+ //| MQL5INDICATORS_PROJECT4.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "ForexYMN" #property link "crownsoyin@gmail.com" #property version "1.00" #include <Trade/Trade.mqh> CTrade trade; // Magic number input int MagicNumber = 1111; input double account_balance = 1000; // Account Balance input double percentage_risk = 2.0; // How many percent of the account do you want to risk per trade? input bool allow_modify = false; // Do you allow break even modifications? input int rrr = 3; // Choose Risk Reward Ratio int rsi_handle; double rsi_buffer[]; double open[]; double close[]; double high[]; double low[]; datetime time[]; double max_high = 0; datetime min_time1 = 0; double min_low = 0; datetime min_time2 = 0; datetime time_low = 0; datetime times_high = 0; string high_obj_name = "High_Line"; string low_obj_name = "Low_Line"; long chart_id; double take_profit; double ask_price = 0; double lot_size; double risk_Amount; double points_risk; // Risk modification double positionProfit = 0; double positionopen = 0; double positionTP = 0; double positionSL = 0; double modifyLevel = 0.0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Configure RSI buffer as a series for easier indexing ArraySetAsSeries(rsi_buffer, true); // Initialize RSI handle for the current symbol, timeframe, and parameters rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE); // Configure candlestick arrays as series ArraySetAsSeries(open, true); ArraySetAsSeries(close, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(time, true); // Set the magic number trade.SetExpertMagicNumber(MagicNumber); return (INIT_SUCCEEDED); // Indicate successful initialization } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { int currBars = iBars(_Symbol,_Period); static int prevBars = currBars; if(prevBars == currBars) return; prevBars = currBars; // Copy RSI values from the indicator into the buffer CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer); // Copy candlestick data CopyOpen(_Symbol, PERIOD_CURRENT, 1, 100, open); CopyClose(_Symbol, PERIOD_CURRENT, 1, 100, close); CopyHigh(_Symbol, PERIOD_CURRENT, 1, 100, high); CopyLow(_Symbol, PERIOD_CURRENT, 1, 100, low); CopyTime(_Symbol, PERIOD_CURRENT, 1, 100, time); static double max_high_static = max_high; static datetime min_time1_static = min_time1; static double min_low_static = min_low; static datetime min_time2_static = min_time2; //GETTING TOTAL POSITIONS int totalPositions = 0; for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(PositionSelectByTicket(ticket)) { if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id)) { totalPositions++; } } } ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK); if(totalPositions < 1) { if(((low[0] < min_low_static && close[0] > min_low_static && close[0] > open[0]) || (low[1] < min_low_static && close[0] > min_low_static && close[0] > open[0]) || (low[2] < min_low_static && close[0] > min_low_static && close[0] > open[0] && close[1] < open[1]))) { take_profit = (close[0] - low[0]) * rrr + close[0]; points_risk = close[0] - low[0]; double riskAmount = account_balance * (percentage_risk / 100.0); double minus = NormalizeDouble(close[0] - low[0],5); lot_size = CalculateLotSize(_Symbol, riskAmount, minus); trade.Buy(lot_size,_Symbol,ask_price, low[0], take_profit); } else if(((high[0] > max_high_static && close[0] < max_high_static && close[0] < open[0]) || (high[1] > max_high_static && close[0] < max_high_static && close[0] < open[0]) || (high[2] > max_high_static && close[0] < max_high_static && close[0] < open[0] && close[1] > open[1]))) { take_profit = MathAbs((high[0] - close[0]) * rrr - close[0]); // Adjusted take-profit calculation points_risk = MathAbs(high[0] - close[0]); double riskAmount = account_balance * (percentage_risk / 100.0); double minus = NormalizeDouble(high[0] - close[0],5); lot_size = CalculateLotSize(_Symbol, riskAmount, minus); trade.Sell(lot_size,_Symbol,ask_price, high[0], take_profit); } } //CHART ID chart_id = ChartID(); int total_bar_high = Bars(_Symbol,PERIOD_CURRENT,min_time1_static,TimeCurrent()); for(int i = 0; i < 12; i++) { if(close[i] < open[i] && close[i+1] > open[i+1]) { max_high = (double)MathMax(high[i],high[i+1]); min_time1 = (datetime)MathMin(time[i],time[i+1]); break; } } int total_bar_low = Bars(_Symbol,PERIOD_CURRENT,min_time2_static,TimeCurrent()); for(int i = 0; i < 12; i++) { if(close[i] > open[i] && close[i+1] < open[i+1]) { min_low = (double)MathMin(low[i],low[i+1]); min_time2 = (datetime)MathMin(time[i],time[i+1]); break; } } for(int i = 0; i < 12; i++) { if(rsi_buffer[i+1] < 30 && rsi_buffer[i] > rsi_buffer[i+1]) { time_low = time[i+1]; break; } } for(int i = 0; i < 12; i++) { if(rsi_buffer[i+1] > 70 && rsi_buffer[i] < rsi_buffer[i+1]) { times_high = time[i+1]; break; } } if((total_bar_high == 0 || total_bar_high > 12) && (min_time1 == times_high)) { max_high_static = max_high; min_time1_static = min_time1; } else if(min_time1 != times_high && total_bar_high > 13) { max_high_static = 0; min_time1_static = 0; } if((total_bar_low == 0 || total_bar_low > 12) && (min_time2 == time_low)) { min_low_static = min_low; min_time2_static = min_time2; } else if(min_time2 != time_low && total_bar_low > 13) { min_low_static = 0; min_time2_static = 0; } ObjectCreate(ChartID(),high_obj_name,OBJ_TREND,0,min_time1_static,max_high_static,TimeCurrent(),max_high_static); ObjectSetInteger(chart_id,high_obj_name,OBJPROP_COLOR,clrGreen); ObjectSetInteger(chart_id,high_obj_name,OBJPROP_WIDTH,3); ObjectCreate(ChartID(),low_obj_name,OBJ_TREND,0,min_time2_static,min_low_static,TimeCurrent(),min_low_static); ObjectSetInteger(chart_id,low_obj_name,OBJPROP_COLOR,clrRed); ObjectSetInteger(chart_id,low_obj_name,OBJPROP_WIDTH,3); if(allow_modify) { for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(PositionSelectByTicket(ticket)) { positionopen = PositionGetDouble(POSITION_PRICE_OPEN); positionTP = PositionGetDouble(POSITION_TP); positionSL = PositionGetDouble(POSITION_SL); positionProfit = PositionGetDouble(POSITION_PROFIT); if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id)) { modifyLevel = MathAbs(NormalizeDouble((positionSL - positionopen) - positionopen,4)); if(ask_price <= modifyLevel) { trade.PositionModify(ticket, positionopen, positionTP); } } if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id)) { modifyLevel = MathAbs(NormalizeDouble((positionopen - positionSL) + positionopen,4)); if(ask_price >= modifyLevel) { trade.PositionModify(ticket, positionopen, positionTP); } } } } } } //+------------------------------------------------------------------+ //| Function to calculate the lot size based on risk amount and stop loss //+------------------------------------------------------------------+ double CalculateLotSize(string symbol, double riskAmount, double stopLossPips) { // Get symbol information double point = SymbolInfoDouble(symbol, SYMBOL_POINT); double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE); // Calculate pip value per lot double pipValuePerLot = tickValue / point; // Calculate the stop loss value in currency double stopLossValue = stopLossPips * pipValuePerLot; // Calculate the lot size double lotSize = riskAmount / stopLossValue; // Round the lot size to the nearest acceptable lot step double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); lotSize = MathFloor(lotSize / lotStep) * lotStep; // Ensure the lot size is within the allowed range double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX); if(lotSize < minLot) lotSize = minLot; if(lotSize > maxLot) lotSize = maxLot; return lotSize; }
Explicação:
input double account_balance = 1000; // Account Balance input double percentage_risk = 2.0; // How many percent of the account do you want to risk per trade? input bool allow_modify = false; // Do you allow break even modifications? input int rrr = 3; // Choose Risk Reward Ratio
As entradas definem parâmetros-chave para gerenciar risco na estratégia de trading. account_balance especifica um saldo de conta fixo, usado para calcular quanto arriscar por operação, garantindo consistência. percentage_risk determina a porcentagem do saldo da conta a arriscar em cada operação, ajudando a controlar a exposição e manter níveis de risco estáveis. rrr (Relação Risco-Retorno) define o retorno desejado em relação ao risco, garantindo que a estratégia busque lucros maiores que as perdas. A entrada allow_modify controla se o stop-loss deve ser movido para o break-even quando uma operação se move a favor da estratégia. Se habilitado, isso protege lucros e reduz o risco das operações vencedoras. Juntas, essas entradas criam uma abordagem disciplinada de trading, garantindo risco consistente por operação e protegendo lucros quando as operações são bem-sucedidas.
take_profit = (close[0] - low[0]) * rrr + close[0]; points_risk = close[0] - low[0]; double riskAmount = account_balance * (percentage_risk / 100.0); double minus = NormalizeDouble(close[0] - low[0],5); lot_size = CalculateLotSize(_Symbol, riskAmount, minus); trade.Buy(lot_size,_Symbol,ask_price, low[0], take_profit);
- take_profit = (close[0] - low[0]) * rrr + close[0]; calcula o nível de take-profit multiplicando a distância entre o preço de fechamento atual e a mínima do candle pela relação risco-retorno (rrr). O resultado é somado ao preço de fechamento para definir um alvo de lucro acima do ponto de entrada.
- points_risk = close[0] - low[0]; calcula a distância de risco do preço de entrada (close) até o stop-loss (low) do candle.
- riskAmount = account_balance * (percentage_risk / 100.0); calcula o montante a arriscar por operação com base no saldo da conta e na porcentagem de risco.
- lot_size = CalculateLotSize(_Symbol, riskAmount, minus); calcula o tamanho do lote com base no montante de risco e na distância do stop-loss (minus), garantindo que a operação esteja alinhada ao risco definido.
Para operações de venda:
- take_profit = MathAbs((high[0] - close[0]) * rrr - close[0]); ajusta o cálculo do take-profit usando a distância entre o preço de fechamento atual e a máxima do candle, novamente considerando a relação risco-retorno.
- points_risk = MathAbs(high[0] - close[0]); calcula a distância de risco para uma operação de venda do ponto de entrada até o nível de stop-loss (máxima do candle).
- O restante da lógica (cálculo do montante de risco e do tamanho do lote) é o mesmo das operações de compra, garantindo gestão de risco consistente para ambos os tipos de operação.
if(allow_modify) { for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(PositionSelectByTicket(ticket)) { positionopen = PositionGetDouble(POSITION_PRICE_OPEN); positionTP = PositionGetDouble(POSITION_TP); positionSL = PositionGetDouble(POSITION_SL); positionProfit = PositionGetDouble(POSITION_PROFIT); if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id)) { modifyLevel = MathAbs(NormalizeDouble((positionSL - positionopen) - positionopen,4)); if(ask_price <= modifyLevel) { trade.PositionModify(ticket, positionopen, positionTP); } } if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id)) { modifyLevel = MathAbs(NormalizeDouble((positionopen - positionSL) + positionopen,4)); if(ask_price >= modifyLevel) { trade.PositionModify(ticket, positionopen, positionTP); } } } } }
O código fornecido lida com a lógica de modificar posições ativas com base em certas condições, focando especificamente na implementação de ajustes de break-even para posições de compra e venda. Aqui está um detalhamento do que o código faz:
Verificando Permissão para Modificar (allow_modify):
- O bloco “if(allow_modify)” garante que a lógica de modificação só seja executada se as modificações de break-even forem permitidas, conforme definido pelo usuário.
- O laço for percorre todas as posições atualmente abertas (PositionsTotal()). Cada posição é verificada para ver se atende às condições de modificação.
Selecionando a Posição:
- O código obtém o ticket de cada posição (PositionGetTicket(i)) e o utiliza para selecionar a posição (PositionSelectByTicket(ticket)).
Obtendo Detalhes da Posição:
Para cada posição selecionada, os seguintes detalhes são recuperados:
- positionopen: O preço no qual a posição foi aberta.
- positionTP: O nível de take-profit da posição.
- positionSL: O nível de stop-loss da posição.
- positionProfit: O lucro atual da posição.
Modificação de Posição de Venda:
- O código verifica se a posição é de venda (POSITION_TYPE_SELL).
- Ele calcula o modifyLevel como a diferença absoluta entre o stop-loss (positionSL) e o preço de abertura (positionopen), e então verifica se o preço de ask atual (ask_price) alcançou ou ultrapassou esse nível.
- Se a condição for atendida, o stop-loss é modificado para o preço de abertura (break-even), mantendo o take-profit inalterado (trade.PositionModify(ticket, positionopen, positionTP)).
- De forma similar, para uma posição de compra (POSITION_TYPE_BUY), calcula-se o modifyLevel como a diferença absoluta entre o preço de abertura e o stop-loss, e verifica-se se o preço de ask se moveu favoravelmente além desse nível.
- Se a condição for atendida, o stop-loss é modificado para o preço de abertura (break-even) e o take-profit é mantido inalterado.
Conclusão
Neste artigo, exploramos o uso de indicadores embutidos no MQL5 usando uma abordagem prática e orientada a projeto. Desenvolvemos um Expert Advisor (EA) para trading automático usando o RSI como exemplo, concentrando-nos em entradas e saídas de operações provocadas por sinais de sobrecompra e sobrevenda. Enfatizamos pontos importantes ao longo do processo, como empregar uma relação risco-retorno (RRR) para alinhar alvos de lucro com riscos calculados e estabelecer uma porcentagem constante de risco por operação para gerenciar diferentes tamanhos de candles. Para tornar a estratégia mais confiável e bem-sucedida, também abordamos varreduras de liquidez e adicionamos ajustes de break-even para travar lucros conforme as operações se desenvolvem. Isso permitiu que o EA se ajustasse a mudanças abruptas no mercado.
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/16514
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Do básico ao intermediário: Classes (II)
Redes neurais em trading: Detecção adaptativa de anomalias de mercado (Conclusão)
Ondas triangulares e em forma de serra: ferramentas para o trader
Desenvolvimento do Conjunto de Ferramentas de Análise de Price Action – Parte (4): Analytics Forecaster EA
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso
Olá Oluwatosin,
Tomei nota de sua solicitação.