Reimaginando Estratégias Clássicas (Parte 12): Estratégia de Breakout EURUSD
Neste artigo, construiremos juntos uma estratégia de negociação em MQL5. Implementaremos uma estratégia de rompimento e a aprimoraremos iterativamente para liberar todo o seu potencial. Vamos discutir algumas das especificações da nossa estratégia.
Focaremos no par EURUSD e negociaremos seus movimentos no período H1. Nossa estratégia de rompimento primeiro registrará os preços atuais de máxima e mínima oferecidos no par EURUSD. Com o passar do tempo, esperaremos que os níveis de preço abram e fechem totalmente fora do canal criado pelo preço máximo e mínimo inicialmente registrados.
Quando isso acontecer, nossa estratégia de negociação terá identificado um viés, indicando que o mercado provavelmente continuará se movendo em uma direção específica. Este não é o ponto em que nossas posições são abertas. Abriremos nossas posições quando nosso viés for confirmado. Assim que os preços abrirem e fecharem completamente além do ponto extremo do candle que rompeu nosso canal inicial, abriremos posições de compra (long) se estivermos acima do canal e posições de venda (short) caso contrário.
Até agora, o sistema que especificamos abrirá muitas negociações. Precisamos definir outras métricas de força ou fraqueza para nos ajudar a filtrar as operações potencialmente não lucrativas. A média móvel pode nos ajudar a identificar rapidamente as tendências de mercado.
Projetaremos nosso sistema para monitorar os preços atuais oferecidos no mercado e observar em qual direção o preço rompe o canal e se esse rompimento é sustentado pela ação de preço subsequente. Se o rompimento observado for consistente com a ação de preço posterior, usaremos as médias móveis para cronometrar a execução das ordens.
Daremos preferência a posições de compra quando a média móvel rápida estiver acima da lenta, e o oposto será verdadeiro para posições de venda. Todas as nossas negociações serão atualizadas dinamicamente usando o indicador Average True Range (ATR) para calcular nossas configurações de stop loss e take profit.
Testaremos nossa estratégia de negociação no período entre 1º de janeiro de 2020 e 30 de novembro de 2024, no período H1.
Nossos indicadores técnicos serão configurados da seguinte forma:
- Média móvel rápida: Média Móvel Exponencial de 5 períodos aplicada ao preço de fechamento.
- Média móvel lenta: Média Móvel Exponencial de 60 períodos aplicada ao preço de fechamento.
- Average True Range: Indicador ATR de 14 períodos.

Fig 1: Estado inicial do nosso aplicativo de negociação de rompimento.
Fig 1: Estado inicial do nosso aplicativo de negociação de rompimento. Esse ponto extremo será o nosso viés — o lado que acreditamos que o mercado seguirá. Nosso viés será confirmado se os níveis de preço fecharem posteriormente abaixo do viés. Caso contrário, nenhuma negociação será aberta.

Fig 2: Nosso aplicativo de negociação identificou um viés de mercado.
Se os níveis de preço confirmarem nosso viés, teremos confiança para abrir uma posição no mercado. Nossa estratégia será inicialmente de acompanhamento de tendência. Portanto, se os preços romperem acima do canal, procuraremos oportunidades de compra.

Fig 3: Nossas posições são abertas após a confirmação do viés.
Introdução ao MQL5
Nosso aplicativo de negociação é construído com lógica de trading e conceitos fundamentais de análise técnica. Vamos destacar os principais elementos contidos no código.
| Parte do Sistema | Finalidade Pretendida |
|---|---|
| Constantes e Parâmetros | Fixaremos certos aspectos do nosso algoritmo de negociação para garantir consistência em todos os testes, como os períodos das médias móveis, o tamanho do lote e a largura do stop loss e do take profit. |
| Variáveis Globais | Essas variáveis são usadas em diferentes partes do código, e é importante garantir que apontem para o mesmo valor em todas as utilizações. Algumas variáveis globais em nosso aplicativo incluem o máximo e o mínimo do canal, a direção que acreditamos que o mercado seguirá (viés) e outros valores de indicadores técnicos. |
Também precisaremos definir outras variáveis importantes em nosso aplicativo de negociação para ajudar a rastrear o estado atual do mercado. Vamos nos familiarizar com as mais relevantes.
| Variável | Finalidade Pretendida |
|---|---|
| Viés | O parâmetro “viés” simboliza a direção na qual os preços parecem estar se movendo; recebe o valor 1 se a tendência for de alta e -1 se for de baixa. Caso contrário, será definido como 0. |
| Médias móveis | A média móvel rápida (ma_f) e a média móvel lenta (ma_s) determinam a tendência. Se ma_f[0] > ma_s[0] e o preço (c) estiver acima da média móvel rápida, uma compra será aberta. Caso contrário, se ma_f[0] < ma_s[0] e o preço estiver abaixo da média móvel lenta, uma venda será aberta. |
| Rompimento | Quando o nível do canal (borda superior ou inferior) é rompido, a direção do movimento (viés) é definida. |
| Níveis de rompimento | O nível de rompimento indicará em qual direção acreditamos que o mercado continuará a seguir no futuro. Se o mercado romper acima do limite superior, nosso sentimento será de alta. |
| Confirmação de sinal | Nossas negociações não serão abertas sem confirmação de sinal. O sinal é confirmado se o mercado mantiver sua direção após o rompimento. Se a confirmação for perdida, a posição poderá ser ajustada ou encerrada. |
| Gerenciamento de Ordens | As negociações que realizaremos dependerão do viés que estivermos observando no mercado. Em caso de tendência de alta (bias == 1), o comando é enviado:
Trade.Buy(vol, Symbol(), ask, channel_low, 0, "Volatility Doctor AI");
Caso contrário, em uma tendência de baixa (bias == -1), o comando é enviado:
Trade.Sell(vol, Symbol(), bid, channel_high, 0, "Volatility Doctor AI"); |
| Stop loss | Inicialmente definido em channel_low para compras e channel_high para vendas, e atualizado posteriormente usando o valor do ATR. |
Agora que temos uma visão conceitual das partes móveis da nossa estratégia, vamos começar a construir nossa estratégia de negociação juntos. Primeiro, devemos especificar os detalhes do nosso aplicativo de trading.
//+------------------------------------------------------------------+ //| MTF Channel 2.mq5 | //| Gamuchirai Zororo Ndawana | //| https://www.mql5.com/en/gamuchiraindawa | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Zororo Ndawana" #property link "https://www.mql5.com/en/gamuchiraindawa" #property version "1.00"
Agora, carregue a biblioteca de negociação.
//+------------------------------------------------------------------+ //| Library | //+------------------------------------------------------------------+ #include <Trade/Trade.mqh> CTrade Trade;
Defina constantes para nosso aplicativo de negociação, como os períodos de alguns dos nossos indicadores técnicos.
//+------------------------------------------------------------------+ //| Constants | //+------------------------------------------------------------------+ const int ma_f_period = 5; //Slow MA const int ma_s_period = 60; //Slow MA
Agora, vamos definir as entradas que o usuário final pode ajustar. Como estamos mantendo nossos indicadores técnicos fixos, o usuário final não ficará sobrecarregado com inúmeros parâmetros.
//+------------------------------------------------------------------+ //| Inputs | //+------------------------------------------------------------------+ input group "Money Management" input int lot_multiple = 5; //Lot Multiple input int atr_multiple = 5; //ATR Multiple
Variáveis globais que utilizaremos na maior parte do nosso programa.
//+------------------------------------------------------------------+ //| Global varaibles | //+------------------------------------------------------------------+ double channel_high = 0; double channel_low = 0; double o,h,l,c; int bias = 0; double bias_level = 0; int confirmation = 0; double vol,bid,ask,initial_sl; int atr_handler,ma_fast,ma_slow; double atr[],ma_f[],ma_s[]; double bo_h,bo_l;
Quando nosso aplicativo de negociação for carregado pela primeira vez, chamaremos uma função especializada para carregar nossos indicadores técnicos e preparar outros dados de mercado necessários.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- setup(); //--- return(INIT_SUCCEEDED); }
Se não estivermos mais usando nosso Expert Advisor, devemos liberar os recursos que não estão mais sendo utilizados.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- IndicatorRelease(atr_handler); IndicatorRelease(ma_fast); IndicatorRelease(ma_slow); }
Sempre que recebermos preços atualizados, atualizaremos nossas variáveis globais e verificaremos novas oportunidades de negociação.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- If we have positions open if(PositionsTotal() > 0) manage_setup(); //--- Keep track of time static datetime timestamp; datetime time = iTime(Symbol(),PERIOD_CURRENT,0); if(timestamp != time) { //--- Time Stamp timestamp = time; if(PositionsTotal() == 0) find_setup(); } }
A função a seguir será responsável por carregar nossos indicadores técnicos e obter os dados de mercado.
//+---------------------------------------------------------------+ //| Load our technical indicators and market data | //+---------------------------------------------------------------+ void setup(void) { channel_high = iHigh(Symbol(),PERIOD_M30,1); channel_low = iLow(Symbol(),PERIOD_M30,1); vol = lot_multiple * SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN); ObjectCreate(0,"Channel High",OBJ_HLINE,0,0,channel_high); ObjectCreate(0,"Channel Low",OBJ_HLINE,0,0,channel_low); atr_handler = iATR(Symbol(),PERIOD_CURRENT,14); ma_fast = iMA(Symbol(),PERIOD_CURRENT,ma_f_period,0,MODE_EMA,PRICE_CLOSE); ma_slow = iMA(Symbol(),PERIOD_CURRENT,ma_s_period,0,MODE_EMA,PRICE_CLOSE); }
Quando nossa estratégia for carregada pela primeira vez, marcaremos os preços máximo e mínimo atuais oferecidos no mercado. Fazendo isso, todos os preços futuros que observaremos poderão ser vistos com contexto — poderemos compará-los com os níveis de preço iniciais observados no momento em que o sistema foi iniciado.
//+---------------------------------------------------------------+ //| Update channel | //+---------------------------------------------------------------+ void update_channel(double new_high, double new_low) { channel_high = new_high; channel_low = new_low; ObjectDelete(0,"Channel High"); ObjectDelete(0,"Channel Low"); ObjectCreate(0,"Channel High",OBJ_HLINE,0,0,channel_high); ObjectCreate(0,"Channel Low",OBJ_HLINE,0,0,channel_low); }
Se tivermos posições abertas, precisaremos atualizar nossos valores de stop loss e take profit conforme necessário. Ajustaremos nossas configurações de risco usando um múltiplo do Average True Range, de modo que nossos parâmetros de risco estejam relacionados aos níveis atuais de volatilidade do mercado.
//+---------------------------------------------------------------+ //| Manage setup | //+---------------------------------------------------------------+ void manage_setup(void) { bid = SymbolInfoDouble(Symbol(),SYMBOL_BID); ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK); CopyBuffer(atr_handler,0,0,1,atr); Print("Managing Position"); if(PositionSelect(Symbol())) { Print("Position Found"); initial_sl = PositionGetDouble(POSITION_SL); } if(bias == 1) { Print("Position Buy"); double new_sl = (ask - (atr[0] * atr_multiple)); Print("Initial: ",initial_sl,"\nNew: ",new_sl); if(initial_sl < new_sl) { Trade.PositionModify(Symbol(),new_sl,0); Print("DONE"); } } if(bias == -1) { Print("Position Sell"); double new_sl = (bid + (atr[0] * atr_multiple)); Print("Initial: ",initial_sl,"\nNew: ",new_sl); if(initial_sl > new_sl) { Trade.PositionModify(Symbol(),new_sl,0); Print("DONE"); } } }
Se não tivermos posições abertas, seguiremos as regras descritas anteriormente para identificar oportunidades de negociação. Lembre-se de que estamos procurando observar uma ação de preço forte rompendo o canal inicial onde o preço se encontrava. Depois disso, ganharemos confiança suficiente para executar a operação se os níveis de preço continuarem se movendo na mesma direção e não cruzarem novamente o canal aberto que acabaram de criar.
//+---------------------------------------------------------------+ //| Find Setup | //+---------------------------------------------------------------+ void find_setup(void) { //--- We are updating the system o = iOpen(Symbol(),PERIOD_CURRENT,1); h = iHigh(Symbol(),PERIOD_CURRENT,1); l = iLow(Symbol(),PERIOD_CURRENT,1); c = iClose(Symbol(),PERIOD_CURRENT,1); bid = SymbolInfoDouble(Symbol(),SYMBOL_BID); ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK); CopyBuffer(atr_handler,0,0,1,atr); CopyBuffer(ma_fast,0,0,1,ma_f); CopyBuffer(ma_slow,0,0,1,ma_s); //--- If we have no market bias if(bias == 0) { //--- Our bias is bullish if ( (o > channel_high) && (h > channel_high) && (l > channel_high) && (c > channel_high) ) { bias = 1; bias_level = h; bo_h = h; bo_l = l; mark_bias(h); } //--- Our bias is bearish if ( (o < channel_low) && (h < channel_low) && (l < channel_low) && (c < channel_low) ) { bias = -1; bias_level = l; bo_h = h; bo_l = l; mark_bias(l); } } //--- Is our bias valid? if(bias != 0) { //--- Our bearish bias has been violated if ( (o > channel_high) && (h > channel_high) && (l > channel_high) && (c > channel_high) && (bias == -1) ) { forget_bias(); } //--- Our bullish bias has been violated if ( (o < channel_low) && (h < channel_low) && (l < channel_low) && (c < channel_low) && (bias == 1) ) { forget_bias(); } //--- Our bullish bias has been violated if ( ((o < channel_high) && (c > channel_low)) ) { forget_bias(); } //--- Check if we have confirmation if((confirmation == 0) && (bias != 0)) { //--- Check if we are above the bias level if ( (o > bias_level) && (h > bias_level) && (l > bias_level) && (c > bias_level) && (bias == 1) ) { confirmation = 1; } //--- Check if we are below the bias level if ( (o < bias_level) && (h < bias_level) && (l < bias_level) && (c < bias_level) && (bias == -1) ) { confirmation = 1; } } } //--- Check if our confirmation is still valid if(confirmation == 1) { //--- Our bias is bullish if(bias == 1) { //--- Confirmation is lost if we fall beneath the breakout level if ( (o < bias_level) && (h < bias_level) && (l < bias_level) && (c < bias_level) ) { confirmation = 0; } } //--- Our bias is bearish if(bias == -1) { //--- Confirmation is lost if we rise above the breakout level if ( (o > bias_level) && (h > bias_level) && (l > bias_level) && (c > bias_level) ) { confirmation = 0; } } } //--- Do we have a setup? if((confirmation == 1) && (bias == 1)) { if(ma_f[0] > ma_s[0]) { if(c > ma_f[0]) { Trade.Buy(vol,Symbol(),ask,channel_low,0,"Volatility Doctor AI"); initial_sl = channel_low; } } } if((confirmation == 1) && (bias == -1)) { if(ma_f[0] < ma_s[0]) { if(c < ma_s[0]) { Trade.Sell(vol,Symbol(),bid,channel_high,0,"Volatility Doctor AI"); initial_sl = channel_high; } } } Comment("O: ",o,"\nH: ",h,"\nL: ",l,"\nC:",c,"\nC H: ",channel_high,"\nC L:",channel_low,"\nBias: ",bias,"\nBias Level: ",bias_level,"\nConfirmation: ",confirmation,"\nMA F: ",ma_f[0],"\nMA S: ",ma_s[0]); }
Quando os níveis de preço romperem o canal inicial, marcaremos o nível de preço extremo criado pelo candle que fez o rompimento. Esse nível extremo é o nosso nível de viés.
//+---------------------------------------------------------------+ //| Mark our bias levels | //+---------------------------------------------------------------+ void mark_bias(double f_level) { ObjectCreate(0,"Bias",OBJ_HLINE,0,0,f_level);the }
Por fim, se os níveis de preço retornarem ao canal de negociação após terem rompido anteriormente, consideraremos o canal antigo inválido e atualizaremos a nova posição do canal para os níveis criados pelo candle de rompimento.
//+---------------------------------------------------------------+ //| Forget our bias levels | //+---------------------------------------------------------------+ void forget_bias() { update_channel(bo_h,bo_l); bias = 0; bias_level = 0; confirmation = 0; ObjectDelete(0,"Bias"); } //+------------------------------------------------------------------+
Agora estamos prontos para realizar o backtest da estratégia de rompimento. Nomeei o aplicativo como "MTF Channel 2", que significa Multiple Time Frame Channel (Canal de Múltiplos Períodos). Selecionei o símbolo EURUSD no período H1. As datas de teste são as mesmas que especificamos anteriormente. O leitor notará que essas três configurações específicas foram mantidas fixas em todos os três testes.

Fig 4: Primeiro conjunto de configurações utilizadas no teste inicial.
Esses não são todos os parâmetros que configuramos. Selecionamos configurações de atraso aleatório (Random delay) para simular cenários reais de negociação, em que a latência pode variar. Também optamos por modelar o teste com base em ticks reais, para tentar obter uma experiência fiel de negociação.

Fig 5: Segundo conjunto de configurações selecionadas para testar nossa estratégia.
Fixaremos as configurações usadas no nosso Expert Advisor para que sejam as mesmas em todos os testes realizados. Manter essas configurações idênticas nos ajudará a isolar a lucratividade proveniente da escolha de regras de negociação mais eficientes.

Fig 6: Nossas configurações de gerenciamento de dinheiro.
Vamos ver nossa estratégia em ação. Na Fig 7 abaixo, podemos ver, no lado direito da captura de tela, as variáveis internas que nossa aplicação está usando para tomar decisões. Observe que todas as nossas operações só serão executadas se a confirmação estiver definida como 1.

Fig 7: Teste de retrocesso (backtest) de nossa estratégia de negociação no par EURUSD.
Infelizmente, podemos ver que nossa estratégia estava perdendo dinheiro. Isso é um sinal de que há espaço para melhorias.

Fig 8: Visualizando o gráfico associado ao nosso backtest.
Vamos obter mais detalhes sobre o teste que acabamos de realizar. Podemos ver claramente que nossa estratégia identificou um total de 53 operações e que 70% delas foram não lucrativas. Nosso índice de Sharpe é negativo. Essas são métricas de desempenho ruins.
Por outro lado, nosso lucro médio é maior que nossa perda média, o que é um bom sinal. Vamos ver como podemos obter um desempenho melhor. Queremos exercer mais controle sobre nossa perda bruta e média, ao mesmo tempo em que maximizamos nosso lucro médio e a proporção de operações lucrativas.

Fig 9: Os detalhes do nosso backtest.
Melhorando nossos primeiros resultados
Enquanto assistia ao backtest, foi frustrante ver o Expert Advisor cometer o mesmo erro repetidamente. A maioria de nossas perdas ocorreu porque estávamos abrindo operações em flutuações insignificantes de preço que apenas por acaso satisfaziam todas as nossas condições. A única solução para isso é selecionar melhores condições que possam naturalmente discriminar movimentos fracos e fortes no mercado.
Uma das opções que temos é comparar o desempenho do EUR e do USD em relação a uma referência comum. Podemos usar a GBP para isso. Vamos comparar como os pares EURGBP e GBPUSD estão se comportando antes de decidirmos abrir uma posição. Ou seja, se em nosso gráfico observarmos que o EURUSD está em uma forte tendência de alta, também gostaríamos de ver o EURGBP se movendo na mesma direção e que o GBPUSD também esteja, idealmente, em tendência de alta.
Em outras palavras, se os níveis de preço do EURUSD nos derem a impressão de que o Euro está se tornando mais caro do que o Dólar, então só ganharemos confiança se também observarmos que o Euro está se valorizando em relação à Libra Esterlina, enquanto o Dólar está simultaneamente se tornando mais barato em relação à mesma. Essa taxa de câmbio triangular de três vias, esperamos, ajudará a identificar falsos rompimentos. Nosso raciocínio é que flutuações que afetam todos os três mercados ao mesmo tempo podem ser movimentos realmente fortes dos quais possamos lucrar.
Adicionaremos algumas linhas de código para modificar a estratégia de negociação original que construímos até agora. Para implementar as mudanças que estamos planejando, primeiro criaremos novas variáveis globais para acompanhar os preços dos pares EURGBP e GBPUSD. Também precisaremos aplicar indicadores técnicos a esses dois outros mercados para que possamos acompanhar as tendências nesses respectivos mercados.
//+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ double channel_high = 0; double channel_low = 0; double o,h,l,c; int bias = 0; double bias_level = 0; int confirmation = 0; double vol,bid,ask,initial_sl; int atr_handler,ma_fast,ma_slow; double atr[],ma_f[],ma_s[]; double bo_h,bo_l; int last_trade_state,current_state; int eurgbp_willr, gbpusd_willr; string symbols[] = {"EURGBP","GBPUSD"};
Quando nosso Expert Advisor for carregado pela primeira vez, precisaremos realizar algumas etapas adicionais para acompanhar a ação de preço que ocorre em nossos símbolos de referência. Essas atualizações serão implementadas na função de configuração (setup function).
//+---------------------------------------------------------------+ //| Load our technical indicators and market data | //+---------------------------------------------------------------+ void setup(void) { //--- Select the symbols we need SymbolSelect("EURGBP",true); SymbolSelect("GBPUSD",true); //--- Reset our last trade state last_trade_state = 0; //--- Mark the current high and low channel_high = iHigh("EURUSD",PERIOD_M30,1); channel_low = iLow("EURUSD",PERIOD_M30,1); ObjectCreate(0,"Channel High",OBJ_HLINE,0,0,channel_high); ObjectCreate(0,"Channel Low",OBJ_HLINE,0,0,channel_low); //--- Our trading volums vol = lot_multiple * SymbolInfoDouble("EURUSD",SYMBOL_VOLUME_MIN); //--- Our technical indicators atr_handler = iATR("EURUSD",PERIOD_CURRENT,14); eurgbp_willr = iWPR(symbols[0],PERIOD_CURRENT,wpr_period); gbpusd_willr = iWPR(symbols[1],PERIOD_CURRENT,wpr_period); ma_fast = iMA("EURUSD",PERIOD_CURRENT,ma_f_period,0,MODE_EMA,PRICE_CLOSE); ma_slow = iMA("EURUSD",PERIOD_CURRENT,ma_s_period,0,MODE_EMA,PRICE_CLOSE); }
Da mesma forma, quando nossa aplicação de negociação não estiver mais em uso, teremos alguns indicadores técnicos adicionais para liberar.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- IndicatorRelease(eurgbp_willr); IndicatorRelease(gbpusd_willr); IndicatorRelease(atr_handler); IndicatorRelease(ma_fast); IndicatorRelease(ma_slow); }
Nossa função OnTick permanecerá a mesma. No entanto, as funções que ela chamará serão modificadas. Primeiro, sempre que atualizarmos nosso canal, precisaremos atualizar 3 canais nos mercados que estamos acompanhando. Um no EURUSD, o segundo no EURGBP e o último no GBPUSD.
//+---------------------------------------------------------------+ //| Update channel | //+---------------------------------------------------------------+ void update_channel(double new_high, double new_low) { channel_high = new_high; channel_low = new_low; ObjectDelete(0,"Channel High"); ObjectDelete(0,"Channel Low"); ObjectCreate(0,"Channel High",OBJ_HLINE,0,0,channel_high); ObjectCreate(0,"Channel Low",OBJ_HLINE,0,0,channel_low); }
A maior parte do programa permaneceu a mesma; a mudança mais significativa que fizemos foi exigir que nossa aplicação de negociação verificasse 2 outros mercados antes de decidir se comprometer com a operação. Se nossos fundamentos nos derem confiança de que o rompimento que estamos vendo no EURUSD pode ser sustentado por força real, então assumiremos a posição. Essas atualizações serão refletidas na função find setup.
Você também notará que a função está chamando uma nova função que não definimos na versão anterior da aplicação de estratégia de rompimento. A função de confirmação adicional verificará os 2 mercados de referência para nossas condições fundamentais de negociação.
//+---------------------------------------------------------------+ //| Find Setup | //+---------------------------------------------------------------+ void find_setup(void) { //--- I have omitted code pieces that were unchanged //--- Do we have a setup? if((confirmation == 1) && (bias == 1) && (current_state != last_trade_state)) { if(ma_f[0] > ma_s[0]) { if(c > ma_f[0]) { if(additional_confirmation(1)) { Trade.Buy(vol,"EURUSD",ask,channel_low,0,"Volatility Doctor"); initial_sl = channel_low; last_trade_state = 1; } } } } if((confirmation == 1) && (bias == -1) && (current_state != last_trade_state)) { if(ma_f[0] < ma_s[0]) { if(c < ma_s[0]) { if(additional_confirmation(-1)) { Trade.Sell(vol,"EURUSD",bid,channel_high,0,"Volatility Doctor"); initial_sl = channel_high; last_trade_state = -1; } } } } }
Essa função deve nos ajudar a distinguir o ruído do mercado de movimentos verdadeiramente fortes. Ao buscar confirmação em outros mercados relacionados, esperamos sempre selecionar as operações mais fortes possíveis.
//+---------------------------------------------------------------+ //| Check for true strength | //+---------------------------------------------------------------+ bool additional_confirmation(int flag) { //--- Do we have additional confirmation from our benchmark pairs? //--- Record the average change in the EURGBP and GBPUSD Market vector eurgbp_willr_f = vector::Zeros(1); vector gbpusd_willr_f = vector::Zeros(1); eurgbp_willr_f.CopyIndicatorBuffer(eurgbp_willr,0,0,1); gbpusd_willr_f.CopyIndicatorBuffer(gbpusd_willr,0,0,1); if((flag == 1) && (eurgbp_willr_f[0] > -50) && (gbpusd_willr_f[0] < -50)) return(true); if((flag == -1) && (eurgbp_willr_f[0] < -50) && (gbpusd_willr_f[0] > -50)) return(true); Print("EURGBP WPR: ",eurgbp_willr_f[0],"\nGBPUSD WPR: ",gbpusd_willr_f[0]); return(false); }
Esta versão da nossa aplicação será intitulada “MTF EURUSD Channel”. A primeira versão que criamos era mais generalizada e poderia facilmente ser usada para negociar qualquer outro símbolo em nosso terminal. No entanto, esta versão usará os pares EURGBP e GBPUSD como referências e, portanto, é mais especializada e destinada apenas a negociar o par EURUSD. O leitor observará que nossas condições de teste são idênticas às do primeiro teste. Realizaremos este backtest usando o mesmo período de tempo e nas mesmas datas do primeiro teste, de 1º de janeiro de 2020 até 30 de novembro de 2024.

Fig 10: O primeiro conjunto de configurações para nosso backtest da estratégia de rompimento de canal EURUSD.
Fig 10: O primeiro conjunto de configurações para nosso backtest da estratégia de rompimento de canal EURUSD. Portanto, não se assuste se o processo levar vários minutos para ser concluído, e não desligue o computador durante o processo.

Fig 11: Precisamos manter o segundo conjunto de configurações idêntico às configurações que usamos no primeiro teste.
Usar um múltiplo de lote igual a 1 significa que todas as minhas operações serão executadas no tamanho mínimo de lote. Se conseguirmos tornar nosso sistema lucrativo com o tamanho mínimo de lote, então aumentar o múltiplo de lote será benéfico. No entanto, se nosso sistema não for lucrativo com o lote mínimo, não ganharemos nada aumentando o tamanho do lote.

Fig 12: Os parâmetros que usaremos para controlar o comportamento da nossa aplicação.
Agora podemos ver como nosso sistema de negociação funciona com dados históricos. Observe que esta versão do nosso sistema monitora 3 mercados ao mesmo tempo. Primeiro, sempre acompanharemos o par EURUSD, para obtermos nossa direção (tendência) a partir dele.

Fig 13: Nosso sistema em ação no par EURUSD.
Nossas posições só podem ser abertas se observarmos os pares EURGBP e GBPUSD apresentando tendências em direções opostas, como mostrado nas Fig 14 e 15 abaixo. Iremos julgar a tendência nos dois mercados usando o Williams Percent Range (WPR). Se o WPR estiver acima do nível 50, consideramos a tendência de alta.

Fig 14: Nosso primeiro par de confirmação, o GBPUSD.
Neste caso, encontramos uma oportunidade de compra no EURUSD. Identificamos essa oportunidade porque as leituras do WPR nos dois mercados estavam em lados opostos do nível 50. Esse desequilíbrio provavelmente será seguido por condições de mercado voláteis — ideais para qualquer estratégia de rompimento (breakout).

Fig 15: Nosso segundo par de referência.
A Fig 15 abaixo mostra como o saldo da nossa conta de negociação simulada muda ao longo do tempo. Nosso objetivo é entender profundamente por que nossa estratégia está falhando, para que possamos tentar melhorar seus pontos fracos.

Fig 16: Gráfico do saldo da nossa conta ao longo do tempo.
Infelizmente, as mudanças que fizemos em nosso sistema reduziram a lucratividade da nossa aplicação de negociação. Nossa perda média e nosso lucro médio aumentaram na mesma proporção. E a proporção de operações lucrativas caiu ligeiramente.

Fig 17: Resultados detalhados do nosso backtest.
Tentativa final de melhoria
Não conseguimos obter melhoria onde mais importa — na lucratividade. Em vez de tentarmos impor nossa visão ao mercado, permitiremos que o computador aprenda a usar as médias móveis melhor do que somos capazes de fazer manualmente. Nossas opiniões sobre como negociar de forma eficaz são enviesadas até certo ponto.
Por outro lado, se permitirmos que o computador aprenda a relação entre o preço de fechamento e a média móvel, então ele poderá criar suas próprias regras de negociação e operar com base no que espera que aconteça em seguida — em vez da forma reativa de negociação que temos praticado até agora.
Para começar, criei um script para nos ajudar a extrair dados históricos de mercado. Basta arrastar e soltar o script no mercado que você deseja negociar para começarmos. O script buscará os dados de mercado para você e também obterá as duas médias móveis que precisamos para nossa estratégia, no mesmo formato usado em nossa aplicação de negociação.
//+------------------------------------------------------------------+ //| ProjectName | //| Copyright 2020, CompanyName | //| http://www.companyname.net | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Zororo Ndawana" #property link "https://www.mql5.com/en/users/gamuchiraindawa" #property version "1.00" #property script_show_inputs //+------------------------------------------------------------------+ //| Script Inputs | //+------------------------------------------------------------------+ input int size = 100000; //How much data should we fetch? //+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ int ma_f_handler,ma_s_handler; double ma_f_reading[],ma_s_reading[]; //+------------------------------------------------------------------+ //| On start function | //+------------------------------------------------------------------+ void OnStart() { //--- Load indicator ma_s_handler = iMA(Symbol(),PERIOD_CURRENT,60,0,MODE_EMA,PRICE_CLOSE); ma_f_handler = iMA(Symbol(),PERIOD_CURRENT,5,0,MODE_EMA,PRICE_CLOSE); //--- Load the indicator values CopyBuffer(ma_f_handler,0,0,size,ma_f_reading); CopyBuffer(ma_s_handler,0,0,size,ma_s_reading); ArraySetAsSeries(ma_f_reading,true); ArraySetAsSeries(ma_s_reading,true); //--- File name string file_name = "Market Data " + Symbol() +" MA Cross" + " As Series.csv"; //--- Write to file int file_handle=FileOpen(file_name,FILE_WRITE|FILE_ANSI|FILE_CSV,","); for(int i= size;i>=0;i--) { if(i == size) { FileWrite(file_handle,"Time","Open","High","Low","Close","MA 5","MA 60"); } else { FileWrite(file_handle,iTime(Symbol(),PERIOD_CURRENT,i), iOpen(Symbol(),PERIOD_CURRENT,i), iHigh(Symbol(),PERIOD_CURRENT,i), iLow(Symbol(),PERIOD_CURRENT,i), iClose(Symbol(),PERIOD_CURRENT,i), ma_f_reading[i], ma_s_reading[i] ); } } //--- Close the file FileClose(file_handle); } //+------------------------------------------------------------------+
Analisando os dados em Python
Agora que você possui os dados de mercado em formato CSV, podemos começar a construir um modelo de IA que, esperamos, nos ajudará a prever falsos rompimentos e evitá-los.
import pandas as pd import numpy as np from sklearn.model_selection import TimeSeriesSplit,cross_val_score from sklearn.linear_model import Ridge from sklearn.metrics import mean_squared_error import matplotlib.pyplot as plt import seaborn as sns
Leia os dados de mercado que extraímos anteriormente. Preste atenção à coluna Time em meu data frame — observe que a última entrada está datada de 18 de abril de 2019. Isso é feito de propósito. Lembre-se de que as datas de início dos testes anteriores começaram em 1º de janeiro de 2020. Isso significa que não estamos nos enganando ao dar ao modelo todas as respostas do teste.
#Define the forecast horizon look_ahead = 24 #Read in the data data = pd.read_csv('Market Data EURUSD MA Cross As Series.csv') #Drop the last 4 years data = data.iloc[:(-24 * 365 * 4),:] data.reset_index(drop=True,inplace=True) #Label the data data['Target'] = data['Close'].shift(-look_ahead) data['MA 5 Target'] = data['MA 5'].shift(-look_ahead) data['MA 5 Close Target'] = data['Target'] - data['MA 5 Target'] data['MA 60 Target'] = data['MA 60'].shift(-look_ahead) data['MA 60 Close Target'] = data['Target'] - data['MA 60 Target'] data.dropna(inplace=True) data.reset_index(drop=True,inplace=True) data

Fig 18: Nossos dados históricos de mercado.
Vamos testar se, no mercado EURUSD, as médias móveis ainda são mais fáceis de prever do que o próprio preço. Para testar nossa hipótese, treinaremos 30 redes neurais idênticas para prever 3 alvos, um de cada vez. Primeiro, iremos prever o preço futuro, a média móvel de 5 períodos e a média móvel de 60 períodos. Todos os alvos serão projetados 24 passos à frente no tempo. Primeiro, registraremos nossa precisão ao prever o preço diretamente.
#Classical error classical_error = [] epochs = 1000 for i in np.arange(0,30): model = MLPRegressor(hidden_layer_sizes=(10,4),max_iter=epochs,early_stopping=False,solver='lbfgs') classical_error.append(np.mean(np.abs(cross_val_score(model,data.loc[:,['Open','High','Low','Close']],data.loc[:,'Target'],cv=tscv,scoring='neg_mean_squared_error'))))
Em seguida, registraremos nossa precisão ao prever a média móvel de 5 períodos.
#MA Cross Over error ma_5_error = [] for i in np.arange(0,30): model = MLPRegressor(hidden_layer_sizes=(10,4),max_iter=epochs,early_stopping=False,solver='lbfgs') ma_5_error.append(np.mean(np.abs(cross_val_score(model,data.loc[:,['Open','High','Low','Close','MA 5']],data.loc[:,'MA 5 Target'],cv=tscv,scoring='neg_mean_squared_error'))))
Por fim, registraremos nossa precisão ao prever a média móvel de 60 períodos.
#New error ma_60_error = [] for i in np.arange(0,30): model = MLPRegressor(hidden_layer_sizes=(10,4),max_iter=10000,early_stopping=False,solver='lbfgs') ma_60_error.append(np.mean(np.abs(cross_val_score(model,data.loc[:,['Open','High','Low','Close','MA 60']],data.loc[:,'MA 60 Target'],cv=tscv,scoring='neg_mean_squared_error'))))
Quando plotamos nossos resultados. Como podemos ver na Fig 19 abaixo, prever a média móvel de 60 períodos gerou o maior erro em nosso sistema, enquanto prever a média móvel de 5 períodos produziu menos erro do que prever o preço diretamente.
plt.plot(classical_error) plt.plot(ma_5_error) plt.plot(ma_60_error) plt.legend(['OHLC','MA 5 ','MA 60']) plt.axhline(np.mean(classical_error),color='blue',linestyle='--') plt.axhline(np.mean(ma_5_error),color='orange',linestyle='--') plt.axhline(np.mean(ma_60_error),color='green',linestyle='--') plt.grid() plt.ylabel('Cross Validated Error') plt.xlabel('Iteration') plt.title('Comparing Different The Error Associated With Different Targets') plt.show()

Fig 19: Visualizando o erro associado a diferentes alvos.
Agora vamos tentar exportar um modelo para nossa aplicação de negociação. Importe as bibliotecas necessárias. Importar as bibliotecas necessárias.
import onnx from skl2onnx import convert_sklearn from skl2onnx.common.data_types import FloatTensorType from sklearn.neural_network import MLPRegressor
Especifique os modelos que precisaremos. Usarei 2 modelos para esta tarefa, pois a média móvel de curto período é fácil de prever — usarei um modelo Ridge simples para fazer sua previsão. No entanto, nossa média móvel de 60 períodos mostrou-se desafiadora. Portanto, usarei uma rede neural para prever a média móvel de longo prazo.
ma_5_model = Ridge() ma_5_model.fit(data[['Open','High','Low','Close','MA 5']],data['MA 5 Target']) ma_5_height_model = Ridge() ma_5_height_model.fit(data[['Open','High','Low','Close','MA 5']],data['MA 5 Close Target']) ma_60_model = Ridge() ma_60_model.fit(data[['Open','High','Low','Close','MA 60']],data['MA 60 Target']) ma_60_height_model = Ridge() ma_60_height_model.fit(data[['Open','High','Low','Close','MA 60']],data['MA 60 Close Target'])
Preparar para exportar para ONNX.
initial_type = [('float_input', FloatTensorType([1, 5]))] ma_5_onx = convert_sklearn(ma_5_model, initial_types=initial_type, target_opset=12 ) ma_5_height_onx = convert_sklearn(ma_5_height_model, initial_types=initial_type, target_opset=12 ) ma_60_height_onx = convert_sklearn(ma_60_height_model, initial_types=initial_type, target_opset=12 ) ma_60_onx = convert_sklearn(ma_60_model, initial_types=initial_type, target_opset=12 )
Salvar no formato ONNX.
onnx.save(ma_5_onx,'eurchf_ma_5_model.onnx') onnx.save(ma_60_onx,'eurchf_ma_60_model.onnx') onnx.save(ma_5_height_onx,'eurusd_ma_5_height_model.onnx') onnx.save(ma_60_height_onx,'eurusd_ma_60_height_model.onnx')
Atualizações finais em MQL5
Vamos aplicar nossos novos modelos para ver se eles podem nos ajudar a filtrar falsos rompimentos no mercado. A primeira atualização que precisamos fazer é importar os modelos ONNX que acabamos de criar.
//+------------------------------------------------------------------+ //| MTF Channel 2.mq5 | //| Gamuchirai Zororo Ndawana | //| https://www.mql5.com/en/gamuchiraindawa | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Zororo Ndawana" #property link "https://www.mql5.com/en/gamuchiraindawa" #property version "1.00" //+------------------------------------------------------------------+ //| ONNX Resources | //+------------------------------------------------------------------+ #resource "\\Files\\eurusd_ma_5_model.onnx" as const uchar eurusd_ma_5_buffer[]; #resource "\\Files\\eurusd_ma_60_model.onnx" as const uchar eurusd_ma_60_buffer[]; #resource "\\Files\\eurusd_ma_5_height_model.onnx" as const uchar eurusd_ma_5_height_buffer[]; #resource "\\Files\\eurusd_ma_60_height_model.onnx" as const uchar eurusd_ma_60_height_buffer[];
Em seguida, precisamos criar algumas novas variáveis associadas aos nossos modelos.
//+------------------------------------------------------------------+ //| Global varaibles | //+------------------------------------------------------------------+ int bias = 0; int state = 0; int confirmation = 0; int last_cross_over_state = 0; int atr_handler,ma_fast,ma_slow; int last_trade_state,current_state; long ma_5_model; long ma_60_model; long ma_5_height_model; long ma_60_height_model; double channel_high = 0; double channel_low = 0; double o,h,l,c; double bias_level = 0; double vol,bid,ask,initial_sl; double atr[],ma_f[],ma_s[]; double bo_h,bo_l; vectorf ma_5_forecast = vectorf::Zeros(1); vectorf ma_60_forecast = vectorf::Zeros(1); vectorf ma_5_height_forecast = vectorf::Zeros(1); vectorf ma_60_height_forecast = vectorf::Zeros(1);
Devemos estender a rotina de inicialização para que agora ela configure nossos modelos ONNX automaticamente.
//+---------------------------------------------------------------+ //| Load our technical indicators and market data | //+---------------------------------------------------------------+ void setup(void) { //--- Reset our last trade state last_trade_state = 0; //--- Mark the current high and low channel_high = iHigh("EURUSD",PERIOD_M30,1); channel_low = iLow("EURUSD",PERIOD_M30,1); ObjectCreate(0,"Channel High",OBJ_HLINE,0,0,channel_high); ObjectCreate(0,"Channel Low",OBJ_HLINE,0,0,channel_low); //--- Our trading volums vol = lot_multiple * SymbolInfoDouble("EURUSD",SYMBOL_VOLUME_MIN); //--- Our technical indicators atr_handler = iATR("EURUSD",PERIOD_CURRENT,14); ma_fast = iMA("EURUSD",PERIOD_CURRENT,ma_f_period,0,MODE_EMA,PRICE_CLOSE); ma_slow = iMA("EURUSD",PERIOD_CURRENT,ma_s_period,0,MODE_EMA,PRICE_CLOSE); //--- Setup our ONNX models //--- Define our ONNX model ulong input_shape [] = {1,5}; ulong output_shape [] = {1,1}; //--- Create the model ma_5_model = OnnxCreateFromBuffer(eurusd_ma_5_buffer,ONNX_DEFAULT); ma_60_model = OnnxCreateFromBuffer(eurusd_ma_60_buffer,ONNX_DEFAULT); ma_5_height_model = OnnxCreateFromBuffer(eurusd_ma_5_height_buffer,ONNX_DEFAULT); ma_60_height_model = OnnxCreateFromBuffer(eurusd_ma_60_height_buffer,ONNX_DEFAULT); //--- Store our models in a list long onnx_models[] = {ma_5_model,ma_5_height_model,ma_60_model,ma_60_height_model}; //--- Loop over the models and set them up for(int i = 0; i < 4; i++) { if(onnx_models[i] == INVALID_HANDLE) { Comment("Failed to load AI module correctly: Invalid handle"); } //--- Validate I/O if(!OnnxSetInputShape(onnx_models[i],0,input_shape)) { Comment("Failed to set input shape correctly: Wrong input shape ",GetLastError()," Actual shape: ",OnnxGetInputCount(ma_5_model)); } if(!OnnxSetOutputShape(onnx_models[i],0,output_shape)) { Comment("Failed to load AI module correctly: Wrong output shape ",GetLastError()," Actual shape: ",OnnxGetOutputCount(ma_5_model)); } } }
Se nosso sistema não estiver mais em uso, devemos liberar os recursos que não estamos mais utilizando.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Free the resources we don't need IndicatorRelease(atr_handler); IndicatorRelease(ma_fast); IndicatorRelease(ma_slow); OnnxRelease(ma_5_model); OnnxRelease(ma_5_height_model); OnnxRelease(ma_60_model); OnnxRelease(ma_60_height_model); }
Quando recebermos preços atualizados, a única grande diferença aqui será que também buscaremos obter uma previsão de nossos modelos de IA.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Keep track of time static datetime timestamp; datetime time = iTime(Symbol(),PERIOD_CURRENT,0); if(timestamp != time) { //--- Time Stamp timestamp = time; //--- Update system variables update(); //--- Make a new prediction model_predict(); if(PositionsTotal() == 0) { state = 0; find_setup(); } } //--- If we have positions open if(PositionsTotal() > 0) manage_setup(); }
Precisamos definir a função responsável por buscar uma previsão de nossos modelos ONNX em MQL5.
//+------------------------------------------------------------------+ //| Get a prediction from our model | //+------------------------------------------------------------------+ void model_predict(void) { //--- Moving average inputs float a = (float) ma_f[0]; float b = (float) ma_s[0]; //--- Price quotes float op = (float) iOpen("EURUSD",PERIOD_H1,0); float hi = (float) iHigh("EURUSD",PERIOD_H1,0); float lo = (float) iLow("EURUSD",PERIOD_H1,0); float cl = (float) iClose("EURUSD",PERIOD_H1,0); //--- ONNX inputs vectorf fast_inputs = {op,hi,lo,cl,a}; vectorf slow_inputs = {op,hi,lo,cl,b}; Print("Fast inputs: ",fast_inputs); Print("Slow inputs: ",slow_inputs); //--- Inference OnnxRun(ma_5_model,ONNX_DATA_TYPE_FLOAT,fast_inputs,ma_5_forecast); OnnxRun(ma_5_height_model,ONNX_DATA_TYPE_FLOAT,fast_inputs,ma_5_height_forecast); OnnxRun(ma_60_model,ONNX_DEFAULT,slow_inputs,ma_60_forecast); OnnxRun(ma_60_height_model,ONNX_DATA_TYPE_FLOAT,fast_inputs,ma_60_height_forecast); }
A última alteração que fizemos afeta a forma como nossa estratégia selecionará as operações. Em vez de simplesmente entrar de forma direta, nossa estratégia agora abrirá operações com base na relação que ela aprendeu entre o preço e a média móvel. Nossa aplicação de negociação agora tem a flexibilidade de comprar e vender, mesmo que isso vá contra o viés que acreditamos existir no mercado.
Observe que há uma nova função sendo chamada, valid setup — essa função simplesmente retorna true se nossas condições de rompimento forem verdadeiras.
//+---------------------------------------------------------------+ //| Find a setup | //+---------------------------------------------------------------+ void find_setup(void) { //--- I have skipped parts of the code that remained the same if(valid_setup()) { //--- Both models are forecasting rising prices if((c < (ma_60_forecast[0] + ma_60_height_forecast[0])) && (c < (ma_5_forecast[0] + ma_5_height_forecast[0]))) { if(last_trade_state != 1) { Trade.Buy(vol,"EURUSD",ask,0,0,"Volatility Doctor"); initial_sl = channel_low; last_trade_state = 1; last_cross_over_state = current_state; } } //--- Both models are forecasting falling prices if((c > (ma_60_forecast[0] + ma_60_height_forecast[0])) && (c > (ma_5_forecast[0] + ma_5_height_forecast[0]))) { if(last_trade_state != -1) { Trade.Sell(vol,"EURUSD",bid,0,0,"Volatility Doctor"); initial_sl = channel_high; last_trade_state = -1; last_cross_over_state = current_state; } } }
Verifique se houve rompimento do canal. Se houver, a função retornará true; caso contrário, false.
//+---------------------------------------------------------------+ //| Do we have a valid setup? | //+---------------------------------------------------------------+ bool valid_setup(void) { return(((confirmation == 1) && (bias == -1) && (current_state != last_cross_over_state)) || ((confirmation == 1) && (bias == 1) && (current_state != last_cross_over_state))); }
Acredito que, neste ponto, você já esteja familiarizado com as configurações que especificaremos para nosso backtest. Lembre-se de que é importante manter essas configurações consistentes para podermos isolar as mudanças de lucratividade associadas às modificações que estamos fazendo em nossas regras de negociação.

Fig 20: Algumas das configurações que usaremos para realizar o backtest de nossa última estratégia de negociação.
Lembre-se de que nosso modelo foi treinado apenas até 2019, mas nosso teste começa em 2020. Portanto, estamos simulando de forma realista o que teria acontecido se tivéssemos projetado este sistema no passado.

Fig 21: O segundo conjunto de configurações que usaremos para realizar o backtest de nossa última estratégia de negociação.
Novamente, nossas configurações são as mesmas em todos os três testes.

Fig 22: As configurações que usaremos para controlar nossa aplicação no último teste.
Agora podemos ver nossa aplicação de negociação baseada em modelo em ação no par EURUSD. Lembre-se de que nenhum desses dados foi mostrado aos modelos durante o treinamento.

Fig 23: Nossa versão final da estratégia de rompimento baseada em modelo em ação.
Podemos ver na Fig 23 abaixo que finalmente conseguimos corrigir a característica inclinação negativa que nosso modelo apresentava desde o início, e agora estamos nos tornando mais lucrativos.

Fig 24: Os resultados do backtest ao testar nossa nova estratégia baseada em modelo.
Nosso objetivo era aumentar o lucro médio e diminuir a proporção de operações perdedoras — o que conseguimos. Nossa perda bruta foi de US$ 498 no primeiro teste, US$ 403 no segundo teste e agora está em US$ 298. Ao mesmo tempo, nosso lucro bruto foi de US$ 378 no primeiro teste e está em US$ 341 neste teste final. Claramente, as alterações que fizemos reduziram nossa perda bruta, mantendo o lucro bruto praticamente o mesmo. Em nosso primeiro sistema, 70% de todas as nossas operações foram não lucrativas. No entanto, com nosso novo sistema, apenas 55% de todas as operações foram não lucrativas.

Fig 25: Resultados detalhados do backtest de nossa estratégia baseada em modelo.
Conclusão
Os rompimentos são potencialmente o melhor momento do dia para negociar. O desafio de identificá-los corretamente não deve ser subestimado. Neste artigo, trabalhamos juntos para construir nossa própria estratégia de rompimento. Adicionamos mais filtros à estratégia na tentativa de torná-la mais lucrativa. Pode ser que estratégias de rompimento não sejam ideais para o mercado EURUSD, e talvez precisemos abordá-lo de um ângulo diferente. No entanto, construir com sucesso uma estratégia de rompimento levará mais tempo e esforço do que o compartilhado neste artigo — mas as ideias apresentadas aqui podem ser valiosas em sua jornada rumo ao sucesso.
| Nome do Arquivo | Descrição |
|---|---|
| MQL5 EURUSD AI | Jupyter Notebook usado para construir nosso modelo do mercado EURUSD. |
| EURUSD MA 60 Model | Modelo ONNX usado para prever a média móvel de 60 períodos. |
| EURUSD MA 60 Height Model | Modelo ONNX usado para prever a diferença entre o preço de fechamento futuro e a futura média móvel de 60 períodos. |
| EURUSD MA 5 Model | Modelo ONNX destinado a prever a média móvel de 5 períodos. |
| EURUSD MA 5 Height Model | Modelo ONNX usado para prever a diferença entre o preço de fechamento futuro e a futura média móvel de 5 períodos. |
| MTF Channel 2 | A primeira implementação da nossa estratégia de rompimento. |
| MTF Channel 2 EURUSD | A segunda implementação da nossa estratégia de rompimento, que utilizou confirmação a partir de pares de referência. |
| MTF Channel 2 EURUSD AI | A terceira implementação da nossa estratégia de rompimento, que foi baseada em modelo. |
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/16569
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.
Desenvolvimento de estratégias de trading de tendência baseadas em aprendizado de máquina
Algoritmo de Otimização de Força Central (Central Force Optimization, CFO)
Otimização com neuroboids — Neuroboids Optimization AlgorithmN 2 (NOA2)
Ondas triangulares e em forma de serra: ferramentas para o trader
- 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á Aliasandr, sua pergunta é brilhante, pois você está certo! Seria melhor primeiro otimizar e selecionar o melhor período com base nos dados históricos. Mas essa é uma pergunta diferente por si só e merece atenção aos detalhes.
Abordamos como usar o aprendizado de máquina para a seleção de períodos usando o intervalo percentual de William e também como usar todos os períodos de uma vez usando o aprendizado múltiplo, cada um em seu próprio artigo.