
Construindo um Modelo de Restrição de Tendência com Candlestick (Parte 9): Consultor Especializado em Múltiplas Estratégias (II)
Conteúdo:
- Introdução
- O que é o Canal de Donchian?
- Acessando o Canal de Donchian no MetaTrader 5
- Acessando o código-fonte do Canal de Donchian no MetaEditor
- Implementação das Estratégias de Canal de Donchian dentro do Trend Constraint Expert.
- Pré-visualização do código-fonte do indicador
- Desenvolvimento do código
- Incorporação ao Trend Constraint Expert
- Testes e Resultados
- Conclusão
Introdução
No século XX, Richard Donchian estabeleceu uma estratégia seguidora de tendências por meio de seus estudos dos mercados financeiros, que mais tarde evoluiu para os Canais de Donchian. Discutimos brevemente seu trabalho em um artigo anterior, mas hoje focaremos na implementação das estratégias associadas à sua teoria. De acordo com várias fontes, acredita-se que os canais abrangem múltiplas estratégias dentro de sua estrutura. A abundância de literatura sobre os Canais de Donchian sugere a eficácia contínua dessa teoria no trading moderno. Ao integrar estratégias de Canal de Donchian, buscamos expandir as oportunidades para o nosso Trend Constraint Expert, aumentando tanto sua lucratividade quanto sua adaptabilidade às diversas condições de mercado.
Algumas estratégias populares baseadas no Canal de Donchian disponíveis online incluem a Estratégia de Breakout, Estratégia Crawl e Estratégia de Reversão à Média, entre outras. Traders notáveis, como Rayner Teo, também produziram conteúdos educacionais voltados a ensinar traders a implementar esses canais de forma eficaz.
Os Canais de Donchian estão disponíveis como indicadores gratuitos na plataforma MetaTrader 5, o que nos proporciona uma vantagem significativa neste projeto. Esse acesso nos permite utilizar o código-fonte do indicador e obter percepções mais detalhadas sobre sua estrutura, facilitando a adaptação ao nosso Trend Constraint Expert. À medida que nosso código cresce em complexidade, desenvolveremos a nova estratégia de forma independente antes de integrá-la ao programa principal. Nos próximos segmentos, aprofundaremos nossa compreensão das teorias e aperfeiçoaremos ainda mais nosso algoritmo.
O que é o Canal de Donchian?
O Canal de Donchian é um indicador de análise técnica que consiste em três linhas — banda superior, linha do meio e banda inferior — usadas para traçar as máximas mais altas e as mínimas mais baixas durante o movimento do preço. Os créditos vão para Richard Donchian, um pioneiro no campo do trading seguidor de tendências, como mencionado anteriormente. Aqui está um breve resumo das três linhas: - Banda Superior: Esta linha representa a máxima mais alta em um período especificado (por exemplo, nos últimos 20 períodos).
- Banda Inferior: Esta linha mostra a mínima mais baixa no mesmo período especificado.
- Linha do Meio: Muitas vezes calculada como a média das bandas superior e inferior, esta linha é às vezes usada como ponto de referência.
Linhas do Canal de Donchian
Acessando o Canal de Donchian no MetaTrader 5
Normalmente, é acessado através da janela Navigator, na aba Indicators, como mostrado na imagem abaixo.
MetaTrader 5 Navigator
Uma vez acessado, você pode arrastar o Canal de Donchian para o gráfico onde pretende utilizá-lo, como mostrado na imagem abaixo. Neste exemplo, estamos usando as configurações padrão do canal no índice Volatility 150 (1s).
Adicionando o Canal de Donchian ao gráfico no MetaTrader 5
A razão pela qual primeiro aplicamos o indicador ao gráfico é para estudar a relação entre a ação do preço e o canal. Isso nos ajuda a entender as regras de engajamento antes de começar o desenvolvimento do algoritmo. Em seguida, mostraremos como acessar o código-fonte do indicador no MetaEditor 5.
Acessando o código-fonte do Canal de Donchian no MetaEditor
Para acessar o arquivo-fonte para edição no MetaEditor 5, abra a janela Navigator e encontre o indicador na aba Free Indicators, assim como na plataforma MetaTrader 5. A principal diferença é que aqui estamos trabalhando com o arquivo-fonte, não com a versão compilada. Clique duas vezes no arquivo para visualizar o código. Consulte as imagens abaixo para facilitar o acompanhamento.
Acessando o código-fonte do Canal de Donchian no MetaEditor.
Implementação das Estratégias de Canal de Donchian dentro do Trend Constraint Expert.
É importante destacar os parâmetros orientadores que moldam nossas discussões e estabelecem as regras de engajamento ao incorporar novas ferramentas. Desde o início, sempre definimos as condições restritivas. Por exemplo, só compramos quando há um candlestick D1 de alta, e vendemos quando há um candlestick D1 de baixa. Com isso em mente, primeiro identificaremos as condições restritivas antes de buscar oportunidades de ordem apresentadas pela configuração do canal que estejam alinhadas à tendência do mercado. Por exemplo, em um cenário D1 de alta, focaremos em: - Que o preço toque o limite inferior do canal para acionar ordens de compra com maior probabilidade de vitória
- Rebote na linha do meio, com probabilidade intermediária de vitória
- Rompimento da fronteira superior, para baixa probabilidade de vitória
Apresentei as três ideias na imagem abaixo.
Estratégias de Canal de Donchian
Para esta apresentação, usarei a técnica de breakout, que monitora condições quando o preço do mercado fecha fora dos limites externos do canal. Agora vamos prosseguir para visualizar o código-fonte do indicador e identificar os buffers relevantes.
Pré-visualização do código-fonte do indicador
O indicador padrão do Canal de Donchian está disponível na plataforma MetaTrader 5. Você também pode acessá-lo diretamente no MetaEditor 5 usando os métodos discutidos anteriormente.//+------------------------------------------------------------------+ //| Donchian Channel.mq5 | //| Copyright 2009-2024, MetaQuotes Ltd | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "2009-2024, MetaQuotes Ltd" #property link "http://www.mql5.com" #property description "Donchian Channel" //--- #property indicator_chart_window #property indicator_buffers 3 #property indicator_plots 3 #property indicator_type1 DRAW_LINE #property indicator_color1 clrBlue #property indicator_type2 DRAW_LINE #property indicator_color2 clrGray #property indicator_type3 DRAW_LINE #property indicator_color3 clrRed //--- labels #property indicator_label1 "Upper Donchian" #property indicator_label2 "Middle Donchian" #property indicator_label3 "Lower Donchian" //--- input parameter input int InpDonchianPeriod=20; // period of the channel input bool InpShowLabel =true; // show price of the level //--- indicator buffers double ExtUpBuffer[]; double ExtMdBuffer[]; double ExtDnBuffer[]; //--- unique prefix to identify indicator objects string ExtPrefixUniq; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- define buffers SetIndexBuffer(0, ExtUpBuffer); SetIndexBuffer(1, ExtMdBuffer); SetIndexBuffer(2, ExtDnBuffer); //--- set a 1-bar offset for each line PlotIndexSetInteger(0, PLOT_SHIFT, 1); PlotIndexSetInteger(1, PLOT_SHIFT, 1); PlotIndexSetInteger(2, PLOT_SHIFT, 1); //--- indicator name IndicatorSetString(INDICATOR_SHORTNAME, "Donchian Channel"); //--- number of digits of indicator value IndicatorSetInteger(INDICATOR_DIGITS, _Digits); //--- prepare prefix for objects string number=StringFormat("%I64d", GetTickCount64()); ExtPrefixUniq=StringSubstr(number, StringLen(number)-4); ExtPrefixUniq=ExtPrefixUniq+"_DN"; Print("Indicator \"Donchian Channels\" started, prefix=", ExtPrefixUniq); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- if the indicator has previously been calculated, start from the bar preceding the last one int start=prev_calculated-1; //--- if this is the first calculation of the indicator, then move by InpDonchianPeriod bars form the beginning if(prev_calculated==0) start=InpDonchianPeriod+1; //--- calculate levels for all bars in a loop for(int i=start; i<rates_total; i++) { //--- get max/min values for the last InpDonchianPeriod bars int highest_bar_index=ArrayMaximum(high, i-InpDonchianPeriod+1, InpDonchianPeriod); int lowest_bar_index=ArrayMinimum(low, i-InpDonchianPeriod+1, InpDonchianPeriod);; double highest=high[highest_bar_index]; double lowest=low[lowest_bar_index]; //--- write values into buffers ExtUpBuffer[i]=highest; ExtDnBuffer[i]=lowest; ExtMdBuffer[i]=(highest+lowest)/2; } //--- draw labels on levels if(InpShowLabel) { ShowPriceLevels(time[rates_total-1], rates_total-1); ChartRedraw(); } //--- succesfully calculated return(rates_total); } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- delete all our graphical objects after use Print("Indicator \"Donchian Channels\" stopped, delete all objects with prefix=", ExtPrefixUniq); ObjectsDeleteAll(0, ExtPrefixUniq, 0, OBJ_ARROW_RIGHT_PRICE); ChartRedraw(0); } //+------------------------------------------------------------------+ //| Show prices' levels | //+------------------------------------------------------------------+ void ShowPriceLevels(datetime time, int last_index) { ShowRightPrice(ExtPrefixUniq+"_UP", time, ExtUpBuffer[last_index], clrBlue); ShowRightPrice(ExtPrefixUniq+"_MD", time, ExtMdBuffer[last_index], clrGray); ShowRightPrice(ExtPrefixUniq+"_Dn", time, ExtDnBuffer[last_index], clrRed); } //+------------------------------------------------------------------+ //| Create or Update "Right Price Label" object | //+------------------------------------------------------------------+ bool ShowRightPrice(const string name, datetime time, double price, color clr) { if(!ObjectCreate(0, name, OBJ_ARROW_RIGHT_PRICE, 0, time, price)) { ObjectMove(0, name, 0, time, price); return(false); } //--- make the label size adaptive long scale=2; if(!ChartGetInteger(0, CHART_SCALE, 0, scale)) { //--- output an error message to the Experts journal Print(__FUNCTION__+", ChartGetInteger(CHART_SCALE) failed, error = ", GetLastError()); } int width=scale>1 ? 2:1; // if chart scale > 1, then label size = 2 ObjectSetInteger(0, name, OBJPROP_COLOR, clr); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, name, OBJPROP_WIDTH, width); ObjectSetInteger(0, name, OBJPROP_BACK, false); ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, name, OBJPROP_SELECTED, false); ObjectSetInteger(0, name, OBJPROP_HIDDEN, true); ObjectSetInteger(0, name, OBJPROP_ZORDER, 0); return(true); } //+------------------------------------------------------------------+
O código personalizado acima implementa o Canal de Donchian. Ele calcula e exibe três linhas: a linha superior do canal (representando a máxima mais alta em um período especificado), a linha inferior do canal (representando a mínima mais baixa no mesmo período) e uma linha do meio (a média entre as linhas superior e inferior). O código inclui funções de inicialização para configurar buffers e propriedades do indicador, um loop de cálculo que atualiza as linhas do canal para cada barra no gráfico e funções para gerenciar objetos gráficos e rótulos de preço. De forma geral, ele fornece aos traders uma ferramenta para identificar tendências e potenciais oportunidades de negociação com base em níveis históricos de preço. De forma geral, ele fornece aos traders uma ferramenta para identificar tendências e potenciais oportunidades de negociação com base em níveis históricos de preço.
Para facilitar o entendimento, vamos dividir os trechos de código acima e identificar nossos buffers para posterior desenvolvimento na próxima seção.
Declaração de Buffer:
Existem três buffers ExtUpBuffer[], ExtMdBuffer[] e ExtDnBuffer[] declarados, que armazenam, respectivamente, os valores superior, médio e inferior do Canal de Donchian.
double ExtUpBuffer[]; double ExtMdBuffer[]; double ExtDnBuffer[];
Configuração de Buffer no OnInit:
A função SetIndexBuffer vincula as linhas exibidas no gráfico aos buffers, permitindo que sejam desenhadas e atualizadas no gráfico.
SetIndexBuffer(0, ExtUpBuffer); SetIndexBuffer(1, ExtMdBuffer); SetIndexBuffer(2, ExtDnBuffer);
Buffer Values Calculation in OnCalculate:
Este código calcula os preços mais altos, mais baixos e a média ao longo do período definido e os armazena nos respectivos buffers para cada barra.
for(int i=start; i<rates_total; i++) { //--- calculate highest and lowest for the Donchian period int highest_bar_index = ArrayMaximum(high, i-InpDonchianPeriod+1, InpDonchianPeriod); int lowest_bar_index = ArrayMinimum(low, i-InpDonchianPeriod+1, InpDonchianPeriod); double highest = high[highest_bar_index]; double lowest = low[lowest_bar_index]; //--- assign values to buffers ExtUpBuffer[i] = highest; ExtDnBuffer[i] = lowest; ExtMdBuffer[i] = (highest + lowest) / 2; }
Para gerar um sinal de compra, a estratégia utiliza o buffer superior (ExtUpBuffer), disparando uma compra quando o preço fecha acima da linha superior de Donchian. De forma oposta, um sinal de venda é disparado quando o preço fecha abaixo da linha inferior de Donchian, conforme definido pelo buffer inferior (ExtDnBuffer). Além disso, o canal do meio (ExtMdBuffer) pode atuar como filtro, refinando a estratégia ao restringir as compras apenas para casos em que o preço esteja acima do canal do meio, indicando uma tendência de alta mais forte. Com esse resumo em mente, tenho confiança de que agora podemos prosseguir para o desenvolvimento do nosso Expert Advisor (EA).
Desenvolvimento do código
A disponibilidade do Canal de Donchian como um indicador embutido simplifica nossa tarefa, já que podemos desenvolver um Expert Advisor (EA) que se concentra nos buffers do indicador para gerar sinais de execução de ordens. Como mencionado anteriormente, para maior clareza, primeiro desenvolveremos um EA baseado no Canal de Donchian antes de integrá-lo ao nosso Trend Constraint Expert. Hoje, focaremos na estratégia de breakout usando o Canal de Donchian. A condição de breakout é simples: ela ocorre quando o preço fecha além das bandas extremas do canal. Você pode se referir à imagem anterior, onde explicamos várias estratégias em detalhe.
Para começar, criaremos um novo arquivo no MetaEditor 5, como mostrado na ilustração abaixo. Nomeei-o de "BreakoutEA", já que nosso foco principal será essa estratégia de breakout.
Iniciar um novo programa EA no MetaEditor
Dividi o processo de construção em cinco grandes segmentos, que você pode seguir passo a passo abaixo para compreender todo o desenvolvimento. Inicialmente, ao lançar o EA, começaremos com o modelo básico, deixando outras partes desmarcadas. Abaixo desse modelo, explicaremos os principais componentes que se unirão no final.
Nesse modelo básico, você encontrará propriedades essenciais, como a diretiva (#property strict). Essa diretiva garante que o compilador aplique o uso correto dos tipos de dados, ajudando a evitar erros de programação potenciais causados por incompatibilidades de tipo. Outro aspecto crucial é a inclusão da biblioteca Trade, que fornece as ferramentas necessárias para gerenciar operações de negociação de forma eficiente. Esses passos estabelecem uma base sólida para o processo de desenvolvimento.
//+------------------------------------------------------------------+ //| BreakoutEA.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com/en/users/billionaire2024/seller | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property version "1.00" //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+
1. Inicialização do Expert Advisor
No segmento de inicialização, são definidos parâmetros de entrada para o EA. Esses parâmetros permitem que configuremos o EA de acordo com nossas preferências de negociação. Entradas críticas incluem o período do Canal de Donchian, a relação risco-retorno, o tamanho do lote para as ordens e valores de pips para stop loss e take profit.
// Input parameters input int InpDonchianPeriod = 20; // Period for Donchian Channel input double RiskRewardRatio = 1.5; // Risk-to-reward ratio input double LotSize = 0.1; // Default lot size for trading input double pipsToStopLoss = 15; // Stop loss in pips input double pipsToTakeProfit = 30; // Take profit in pips // Indicator handle storage int handle; string indicatorKey; // Expert initialization function int OnInit() { // Create a unique key for the indicator based on the symbol and period indicatorKey = StringFormat("%s_%d", Symbol(), InpDonchianPeriod); // Load the Donchian Channel indicator handle = iCustom(Symbol(), Period(), "Free Indicators\\Donchian Channel", InpDonchianPeriod); // Check if the indicator loaded successfully if (handle == INVALID_HANDLE) { Print("Failed to load the indicator. Error: ", GetLastError()); return INIT_FAILED; } return INIT_SUCCEEDED; }
Uma chave exclusiva é criada com base no símbolo de negociação e no período definido, garantindo que cada instância do indicador possa ser diferenciada. A função iCustom() é usada para carregar o indicador Canal de Donchian, especificando seu caminho dentro do diretório do MetaTrader (Free Indicators\Donchian Channel). Se o carregamento falhar (indicado por um INVALID_HANDLE), uma mensagem de erro é impressa e a inicialização falha, impedindo a execução sem os dados necessários do indicador. Se o carregamento falhar (indicado por um INVALID_HANDLE), uma mensagem de erro é impressa e a inicialização falha, impedindo a execução sem os dados necessários do indicador. Na maioria dos casos, o EA não será executado se o indicador falhar ao carregar.
//Typical Journal log when the EA fails to locate an indicator in the root indicators storage. 2024.10.20 08:49:04.117 2022.01.01 00:00:00 cannot load custom indicator 'Donchian Channel' [4802] 2024.10.20 08:49:04.118 2022.01.01 00:00:00 indicator create error in 'DonchianEA.mq5' (1,1) 2024.10.20 08:49:04.118 OnInit critical error 2024.10.20 08:49:04.118 tester stopped because OnInit failed
2. Limpeza e Desinicialização
O segmento de limpeza é responsável por liberar os recursos utilizados pelo EA. Isso é feito na função OnDeinit(), chamada quando o EA é removido ou quando o MetaTrader 5 está sendo encerrado. A função garante que o handle do indicador seja liberado usando IndicatorRelease(). A limpeza completa de recursos é essencial para evitar vazamentos de memória e manter a performance geral da plataforma.
// Expert deinitialization function void OnDeinit(const int reason) { // Release the indicator handle to free up resources IndicatorRelease(handle); }
3. Lógica Principal de Execução
A lógica principal de execução reside na função OnTick(), que é acionada a cada tick de mercado ou mudança de preço. Nessa função, é feita uma verificação para ver se existem posições abertas atualmente usando a função PositionsTotal(). Se não houver posições abertas, o programa prossegue para avaliar as condições de negociação invocando uma função separada. Essa estrutura evita que múltiplas ordens sejam abertas de uma só vez, o que poderia levar ao overtrading.
// Main execution function with block-based control void OnTick() { // Check if any positions are currently open if (PositionsTotal() == 0) { CheckTradingConditions(); } }
4. Avaliando Condições de Negociação
Neste segmento, o EA verifica as condições de mercado em relação às bandas superior e inferior do Canal de Donchian. Os buffers do indicador são redimensionados para acomodar os dados mais recentes. A função CopyBuffer() recupera os valores mais atuais do Canal de Donchian.
// Check trading conditions based on indicator buffers void CheckTradingConditions() { double ExtUpBuffer[], ExtDnBuffer[]; // Buffers for upper and lower Donchian bands // Resize buffers to hold the latest data ArrayResize(ExtUpBuffer, 2); ArrayResize(ExtDnBuffer, 2); // Get the latest values from the Donchian Channel if (CopyBuffer(handle, 0, 0, 2, ExtUpBuffer) <= 0 || CopyBuffer(handle, 2, 0, 2, ExtDnBuffer) <= 0) { Print("Error reading indicator buffer. Error: ", GetLastError()); return; } // Get the close price of the current candle double closePrice = iClose(Symbol(), Period(), 0); // Buy condition: Closing price is above the upper Donchian band if (closePrice > ExtUpBuffer[1]) { double stopLoss = closePrice - pipsToStopLoss * _Point; // Calculate stop loss double takeProfit = closePrice + pipsToTakeProfit * _Point; // Calculate take profit OpenBuy(LotSize, stopLoss, takeProfit); } // Sell condition: Closing price is below the lower Donchian band if (closePrice < ExtDnBuffer[1]) { double stopLoss = closePrice + pipsToStopLoss * _Point; // Calculate stop loss double takeProfit = closePrice - pipsToTakeProfit * _Point; // Calculate take profit OpenSell(LotSize, stopLoss, takeProfit); } }
O preço de fechamento atual é obtido, o que é crucial para avaliar sinais de negociação. As condições de trading são definidas de forma que uma ordem de compra seja disparada se o preço de fechamento ultrapassar a banda superior, enquanto uma ordem de venda é colocada se o preço cair abaixo da banda inferior. Os níveis de stop loss e take profit são calculados com base nos valores de pips definidos pelo usuário para gerenciar o risco de forma eficaz.
5. Funções de Colocação de Ordens
As funções de colocação de ordens lidam com a execução de operações de compra e venda. Cada função tenta colocar uma ordem usando métodos da classe CTrade, que simplifica o gerenciamento de transações. Após a tentativa de executar uma ordem, o programa verifica se a operação foi bem-sucedida. Se falhar, uma mensagem de erro é exibida para informar o trader sobre a falha. Essas funções encapsulam a lógica de negociação e fornecem uma interface clara para a execução de ordens com base nas condições estabelecidas anteriormente.
// Open a buy order void OpenBuy(double lotSize, double stopLoss, double takeProfit) { // Attempt to open a buy order if (trade.Buy(lotSize, Symbol(), 0, stopLoss, takeProfit, "Buy Order")) { Print("Buy order placed: Symbol = ", Symbol(), ", LotSize = ", lotSize); } else { Print("Failed to open buy order. Error: ", GetLastError()); } } // Open a sell order void OpenSell(double lotSize, double stopLoss, double takeProfit) { // Attempt to open a sell order if (trade.Sell(lotSize, Symbol(), 0, stopLoss, takeProfit, "Sell Order")) { Print("Sell order placed: Symbol = ", Symbol(), ", LotSize = ", lotSize); } else { Print("Failed to open sell order. Error: ", GetLastError()); } }
Nosso EA de breakout com Canal de Donchian agora está totalmente desenvolvido e pronto, tudo em um só lugar.
//+----------------------------------------------------------------------+ //| BreakoutEA.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com/en/users/billionaire2024/seller | //+----------------------------------------------------------------------+ #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property version "1.00" #property strict #include <Trade\Trade.mqh> // Include the trade library // Input parameters input int InpDonchianPeriod = 20; // Period for Donchian Channel input double RiskRewardRatio = 1.5; // Risk-to-reward ratio input double LotSize = 0.1; // Default lot size for trading input double pipsToStopLoss = 15; // Stop loss in pips input double pipsToTakeProfit = 30; // Take profit in pips // Indicator handle storage int handle; string indicatorKey; double ExtUpBuffer[]; // Upper Donchian buffer double ExtDnBuffer[]; // Lower Donchian buffer // Trade instance CTrade trade; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { indicatorKey = StringFormat("%s_%d", Symbol(), InpDonchianPeriod); // Create a unique key for the indicator handle = iCustom(Symbol(), Period(), "Free Indicators\\Donchian Channel", InpDonchianPeriod); if (handle == INVALID_HANDLE) { Print("Failed to load the indicator. Error: ", GetLastError()); return INIT_FAILED; } return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Release the indicator handle IndicatorRelease(handle); } //+------------------------------------------------------------------+ //| Main execution function with block-based control | //+------------------------------------------------------------------+ void OnTick() { // Check if any positions are currently open if (PositionsTotal() == 0) { CheckTradingConditions(); } } //+------------------------------------------------------------------+ //| Check trading conditions based on indicator buffers | //+------------------------------------------------------------------+ void CheckTradingConditions() { // Resize buffers to get the latest data ArrayResize(ExtUpBuffer, 2); ArrayResize(ExtDnBuffer, 2); // Get the latest values from the Donchian Channel if (CopyBuffer(handle, 0, 0, 2, ExtUpBuffer) <= 0 || CopyBuffer(handle, 2, 0, 2, ExtDnBuffer) <= 0) { Print("Error reading indicator buffer. Error: ", GetLastError()); return; } // Get the close price of the current candle double closePrice = iClose(Symbol(), Period(), 0); // Buy condition: Closing price is above the upper Donchian band if (closePrice > ExtUpBuffer[1]) { double stopLoss = closePrice - pipsToStopLoss * _Point; // Calculate stop loss double takeProfit = closePrice + pipsToTakeProfit * _Point; // Calculate take profit OpenBuy(LotSize, stopLoss, takeProfit); } // Sell condition: Closing price is below the lower Donchian band if (closePrice < ExtDnBuffer[1]) { double stopLoss = closePrice + pipsToStopLoss * _Point; // Calculate stop loss double takeProfit = closePrice - pipsToTakeProfit * _Point; // Calculate take profit OpenSell(LotSize, stopLoss, takeProfit); } } //+------------------------------------------------------------------+ //| Open a buy order | //+------------------------------------------------------------------+ void OpenBuy(double lotSize, double stopLoss, double takeProfit) { if (trade.Buy(lotSize, Symbol(), 0, stopLoss, takeProfit, "Buy Order")) { Print("Buy order placed: Symbol = ", Symbol(), ", LotSize = ", lotSize); } else { Print("Failed to open buy order. Error: ", GetLastError()); } } //+------------------------------------------------------------------+ //| Open a sell order | //+------------------------------------------------------------------+ void OpenSell(double lotSize, double stopLoss, double takeProfit) { if (trade.Sell(lotSize, Symbol(), 0, stopLoss, takeProfit, "Sell Order")) { Print("Sell order placed: Symbol = ", Symbol(), ", LotSize = ", lotSize); } else { Print("Failed to open sell order. Error: ", GetLastError()); } } //+------------------------------------------------------------------+
Vamos testar o Breakout EA antes de incorporá-lo ao código principal do Trend Constraint. É importante observar que esta não é a versão final, pois ainda precisamos implementar a lógica de restrição alinhada aos nossos objetivos gerais.
Adicionando BreakoutEA ao gráfico
Clique com o botão direito na lista de Expert Advisors e selecione "Test" para abrir a janela de testes. A partir daí, você pode escolher e configurar o BreakoutEA para testes. Veja a performance abaixo.
BreakoutEA em execução no Strategy Tester
Muito bem! Executamos ordens com sucesso, o que é uma grande conquista. Agora podemos usar essa base para aumentar a lucratividade e filtrar trades desnecessários. Isso destaca a importância da próxima etapa, onde incorporaremos restrições para eliminar operações de menor probabilidade.Incorporação ao Trend Constraint Expert
Unir dois códigos de EA envolve combinar as funções de ambos os lados, com as funções compartilhadas tornando-se primárias no EA final. Além disso, funções exclusivas de cada EA expandirão o tamanho e as capacidades gerais do código mesclado. Por exemplo, existem propriedades que existem tanto no Trend Constraint Expert quanto no Breakout EA, e nós as incorporaremos em um único programa. Consulte o trecho de código abaixo, que destaca essas propriedades compartilhadas.
// We merge it to one #property strict #include <Trade\Trade.mqh> // Include the trade library
Agora, vamos fazer a incorporação. Ao mesclar duas ou mais estratégias em um único Expert Advisor (EA), as funções principais que permanecem centrais para a lógica de negociação geral incluem OnInit(), OnTick() e as funções de gerenciamento de posição (como OpenBuy() e OpenSell()). Essas funções atuam como o núcleo do EA, lidando respectivamente com a inicialização de indicadores, análise em tempo real do mercado e colocação de ordens.
Enquanto isso, os recursos da estratégia de breakout do Canal de Donchian e da estratégia seguidora de tendência com RSI e médias móveis no Trend Constraint Expert tornam-se extensões do programa existente, incorporados como condições distintas dentro da função OnTick(). O EA avalia simultaneamente tanto os sinais de breakout do Canal de Donchian quanto os sinais de tendência do RSI e das médias móveis, permitindo reagir de forma mais abrangente às condições de mercado.
Ao integrar essas funcionalidades independentes, o EA aprimora sua capacidade de tomada de decisão, resultando em uma estratégia mais robusta, capaz de se adaptar a diferentes dinâmicas do mercado.
1. Funções de Inicialização
A função de inicialização (<i0>OnInit()</i0>) é crucial, pois configura os indicadores necessários para ambas as estratégias de negociação que o Expert Advisor irá empregar. Essa função é chamada quando o EA é carregado pela primeira vez e garante que todos os componentes essenciais estejam prontos antes de qualquer operação de trading começar. Ela inicializa o RSI (Relative Strength Index) e o indicador Canal de Donchian. Se algum desses indicadores falhar em inicializar corretamente, a função retornará um status de erro, impedindo o sistema de negociação de rodar e evitando riscos potenciais no mercado.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Initialize RSI handle rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE); if (rsi_handle == INVALID_HANDLE) { Print("Failed to create RSI indicator handle"); return INIT_FAILED; } // Create a handle for the Donchian Channel handle = iCustom(Symbol(), Period(), "Free Indicators\\Donchian Channel", InpDonchianPeriod); if (handle == INVALID_HANDLE) { Print("Failed to load the Donchian Channel indicator. Error: ", GetLastError()); return INIT_FAILED; } return INIT_SUCCEEDED; }
2. Lógica Principal de Execução
A lógica principal de execução é tratada na função OnTick(), chamada sempre que há um tick de mercado. Essa função serve como o coração do EA, orquestrando a avaliação de várias estratégias de negociação em resposta às condições de mercado em mudança. Ela chama, em sequência, as funções responsáveis por verificar a estratégia seguidora de tendência e a estratégia de breakout. Além disso, inclui uma verificação de ordens expiradas, permitindo que o EA gerencie riscos de forma eficaz, garantindo que nenhuma posição antiga permaneça ativa.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Execute both strategies independently on each tick CheckTrendConstraintTrading(); CheckBreakoutTrading(); CheckOrderExpiration(); // Check for expired Trend Following orders }
3. Funções Modulares de Condição de Trading
Estratégia Seguidora de Tendência:
Esta função verifica se há posições abertas antes de prosseguir com a tomada de decisão. Se não houver posições abertas, ela recupera o valor atual do RSI e calcula as médias móveis de curto e longo prazo para determinar a tendência do mercado. Se o mercado estiver em tendência de alta e o RSI indicar condições de sobrevenda, poderá ser colocada uma ordem de compra. Por outro lado, se o mercado estiver em tendência de baixa e o RSI sinalizar condições de sobrecompra, poderá ser colocada uma ordem de venda. Se já houver posições abertas, a função as gerencia com um mecanismo de trailing stop.
//+------------------------------------------------------------------+ //| Check and execute Trend Constraint EA trading logic | //+------------------------------------------------------------------+ void CheckTrendConstraintTrading() { // Check if there are any positions open if (PositionsTotal() == 0) { // Get RSI value double rsi_value; double rsi_values[]; if (CopyBuffer(rsi_handle, 0, 0, 1, rsi_values) <= 0) { Print("Failed to get RSI value"); return; } rsi_value = rsi_values[0]; // Calculate moving averages double ma_short = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE); double ma_long = iMA(_Symbol, PERIOD_CURRENT, 200, 0, MODE_EMA, PRICE_CLOSE); // Determine trend direction bool is_uptrend = ma_short > ma_long; bool is_downtrend = ma_short < ma_long; // Check for buy conditions if (is_uptrend && rsi_value < RSI_Oversold) { double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); double stopLossPrice = currentPrice - StopLoss * _Point; double takeProfitPrice = currentPrice + TakeProfit * _Point; // Attempt to open a Buy order if (trade.Buy(Lots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Trend Following Buy") > 0) { Print("Trend Following Buy order placed."); } else { Print("Error placing Trend Following Buy order: ", GetLastError()); } } // Check for sell conditions else if (is_downtrend && rsi_value > RSI_Overbought) { double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double stopLossPrice = currentPrice + StopLoss * _Point; double takeProfitPrice = currentPrice - TakeProfit * _Point; // Attempt to open a Sell order if (trade.Sell(Lots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Trend Following Sell") > 0) { Print("Trend Following Sell order placed."); } else { Print("Error placing Trend Following Sell order: ", GetLastError()); } } } else { // Implement Trailing Stop for open positions TrailingStopLogic(); } }
Função de Estratégia de Breakout:
Essa função é projetada para avaliar condições de breakout com base no indicador Canal de Donchian. Ela redimensiona os buffers necessários para capturar os dados mais recentes e verifica oportunidades potenciais de breakout. A estratégia procura por níveis de preço específicos a serem superados, o que pode indicar um movimento significativo em uma direção. Quando essas condições são atendidas, o EA executa ordens de acordo.
/+-------------------------------------------------------------------+ //| Check and execute Breakout EA trading logic | //+------------------------------------------------------------------+ void CheckBreakoutTrading() { // Resize buffers to get the latest data ArrayResize(ExtUpBuffer, 2); ArrayResize(ExtDnBuffer, 2); // Get the latest values from the Donchian Channel if (CopyBuffer(handle, 0, 0, 2, ExtUpBuffer) <= 0 || CopyBuffer(handle, 2, 0, 2, ExtDnBuffer) <= 0) { Print("Error reading Donchian Channel buffer. Error: ", GetLastError()); return; } // Get the close price of the current candle double closePrice = iClose(Symbol(), Period(), 0); // Get the daily open and close for the previous day double lastOpen = iOpen(Symbol(), PERIOD_D1, 1); double lastClose = iClose(Symbol(), PERIOD_D1, 1); // Determine if the last day was bullish or bearish bool isBullishDay = lastClose > lastOpen; // Bullish if close > open bool isBearishDay = lastClose < lastOpen; // Bearish if close < open // Check if there are any open positions before executing breakout strategy if (PositionsTotal() == 0) // Only proceed if no positions are open { // Buy condition: Closing price is above the upper Donchian band on a bullish day if (closePrice > ExtUpBuffer[1] && isBullishDay) { double stopLoss = closePrice - pipsToStopLoss * _Point; // Calculate stop loss double takeProfit = closePrice + pipsToTakeProfit * _Point; // Calculate take profit OpenBreakoutBuyOrder(stopLoss, takeProfit); } // Sell condition: Closing price is below the lower Donchian band on a bearish day if (closePrice < ExtDnBuffer[1] && isBearishDay) { double stopLoss = closePrice + pipsToStopLoss * _Point; // Calculate stop loss double takeProfit = closePrice - pipsToTakeProfit * _Point; // Calculate take profit OpenBreakoutSellOrder(stopLoss, takeProfit); } } }
Verificação de Trading de Breakout:
Essa função recupera os valores mais recentes do indicador Canal de Donchian para analisar as condições de mercado. Ela determina o preço de fechamento da vela atual e os preços de abertura e fechamento diários do dia anterior. Com base na comparação desses preços, identifica se o dia anterior foi de alta ou de baixa. A função então verifica se existem posições abertas antes de executar a estratégia de breakout. Se não houver posições abertas, verifica as condições para abrir uma ordem de compra (se o preço de fechamento estiver acima da banda superior em um dia de alta) ou uma ordem de venda (se o preço de fechamento estiver abaixo da banda inferior em um dia de baixa). Ela calcula os níveis de stop loss e take profit para cada trade antes de tentar colocar a ordem.
//+------------------------------------------------------------------+ //| Open a buy order for the Breakout strategy | //+------------------------------------------------------------------+ void OpenBreakoutBuyOrder(double stopLoss, double takeProfit) { if (trade.Buy(LotSize, _Symbol, 0, stopLoss, takeProfit, "Breakout Buy")) { Print("Breakout Buy order placed."); } else { Print("Error placing Breakout Buy order: ", GetLastError()); } } //+------------------------------------------------------------------+ //| Open a sell order for the Breakout strategy | //+------------------------------------------------------------------+ void OpenBreakoutSellOrder(double stopLoss, double takeProfit) { if (trade.Sell(LotSize, _Symbol, 0, stopLoss, takeProfit, "Breakout Sell")) { Print("Breakout Sell order placed."); } else { Print("Error placing Breakout Sell order: ", GetLastError()); } }
4. Verificação de Expiração de Ordens
A função CheckOrderExpiration revisa todas as posições abertas para identificar e fechar aquelas que excederam um tempo de vida especificado. Essa funcionalidade é crucial para manter um ambiente de trading saudável, gerenciar riscos de forma eficaz e evitar que posições antigas permaneçam abertas por mais tempo do que o estrategicamente aconselhável. A função verifica o número mágico de cada posição para determinar se faz parte da estratégia seguidora de tendência e compara o horário atual com o horário de abertura da posição para decidir se ela deve ser encerrada.
//+------------------------------------------------------------------+ //| Check for expired Trend Following orders | //+------------------------------------------------------------------+ void CheckOrderExpiration() { for (int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if (PositionSelectByTicket(ticket)) { long magicNumber = PositionGetInteger(POSITION_MAGIC); // Check if it's a Trend Following position if (magicNumber == MagicNumber) { datetime openTime = (datetime)PositionGetInteger(POSITION_TIME); if (TimeCurrent() - openTime >= OrderLifetime) { // Attempt to close the position if (trade.PositionClose(ticket)) { Print("Trend Following position closed due to expiration."); } else { Print("Error closing position due to expiration: ", GetLastError()); } } } } } }
5. Lógica de Trailing Stop
O método TrailingStopLogic é responsável por gerenciar posições abertas ajustando seus níveis de stop-loss conforme as regras do trailing stop. Para posições compradas, ele move o stop loss para cima se o preço atual exceder o limite definido de trailing stop. Para posições vendidas, ele abaixa o stop loss quando as condições são atendidas. Essa abordagem ajuda a proteger lucros, permitindo que o stop loss acompanhe movimentos favoráveis de preço, reduzindo o risco de perda caso o mercado reverta.
//+------------------------------------------------------------------+ //| Manage trailing stops for open positions | //+------------------------------------------------------------------+ void TrailingStopLogic() { for (int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if (PositionSelectByTicket(ticket)) { double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); double stopLoss = PositionGetDouble(POSITION_SL); // Update stop loss for long positions if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { if (currentPrice - stopLoss > TrailingStop * _Point || stopLoss == 0) { trade.PositionModify(ticket, currentPrice - TrailingStop * _Point, PositionGetDouble(POSITION_TP)); } } // Update stop loss for short positions else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { if (stopLoss - currentPrice > TrailingStop * _Point || stopLoss == 0) { trade.PositionModify(ticket, currentPrice + TrailingStop * _Point, PositionGetDouble(POSITION_TP)); } } } } }
6. Função de Limpeza
A função OnDeinit() atua como rotina de limpeza, executada quando o Expert Advisor (EA) é removido do gráfico. Essa função lida com a liberação de quaisquer recursos alocados ou handles de indicadores, garantindo que não haja vazamentos de memória ou referências pendentes. Ela confirma que o EA foi devidamente desinicializado e registra essa ação.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Release indicators and handles IndicatorRelease(rsi_handle); IndicatorRelease(handle); Print("Expert deinitialized."); }
Testes e Resultados
Aqui está nossa experiência no Strategy Tester na imagem abaixo com os novos recursos funcionando.
Trend Constraint Expert: Estratégias de Breakout de Canal de Donchian e Seguidor de Tendência
Conclusão
Nossa discussão abordou o desafio de se adaptar a diferentes condições de mercado incorporando múltiplas estratégias em um único Expert Advisor (EA). Inicialmente, desenvolvemos um mini EA de breakout para gerenciar efetivamente o processo de rompimento antes de integrá-lo ao nosso Trend Constraint Expert principal. Essa integração aprimorou a funcionalidade do EA ao alinhar a estratégia de breakout com o sentimento de mercado em timeframes maiores, especificamente a análise de candlestick D1, o que reduziu a execução excessiva de ordens.
Cada trade executado no Strategy Tester foi claramente anotado com a estratégia empregada, proporcionando transparência e clareza na compreensão da mecânica em ação. Posso dizer que, para enfrentar um problema complexo de forma eficaz, divida-o em componentes menores e gerenciáveis e trate cada um passo a passo — foi isso que fizemos ao desenvolver um mini EA antes de incorporá-lo em um só.
Embora nossa implementação demonstre potencial, ela continua sendo um trabalho em andamento, com espaço para melhorias. O EA serve como uma ferramenta educacional, demonstrando como diferentes estratégias podem funcionar em conjunto para melhores resultados de negociação. Eu o encorajo a experimentar os EAs fornecidos e modificá-los para se ajustarem às suas estratégias de trading. Seu feedback é essencial à medida que exploramos coletivamente as possibilidades de combinar múltiplas estratégias no trading algorítmico.
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/16137
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.





- 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
Confira o novo artigo: Construindo um modelo de restrição de tendência de candlestick (Parte 9): Expert Advisor de estratégias múltiplas (II).
Autor: Clemence Benjamin