English Русский 中文 Español 日本語
preview
Repensando as estratégias clássicas (Parte 15): Estratégia de rompimento da faixa de preços do dia anterior

Repensando as estratégias clássicas (Parte 15): Estratégia de rompimento da faixa de preços do dia anterior

MetaTrader 5 — Exemplos |
20 0
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

Nesta série de artigos, frequentemente recorremos a modelos de aprendizado de máquina para melhorar o desempenho de nossas estratégias de trading. No entanto, muitas vezes ignoramos o fato de que esses modelos se baseiam em diversos pressupostos não verificados sobre os dados disponíveis. Além disso, o aprendizado estatístico clássico não nos oferece orientações sobre como demonstrar se as relações que tentamos modelar realmente existem no mercado.

Por outro lado, ao longo de muitos anos de experiência nos mercados, os traders desenvolveram uma compreensão de certos princípios fundamentais que podemos chamar de lógica de mercado. Esse entendimento, ao qual nos referiremos como lógica de mercado, só pode ser adquirido por meio da experiência. Nossa expectativa é aproveitar o conhecimento acumulado por traders experientes em nossos sistemas de trading algorítmico. Afinal, os traders discricionários já atuavam intensamente nos mercados financeiros muito antes do surgimento dos computadores. Portanto, é possível que as regras empíricas desenvolvidas por eles contenham fundamentos válidos que mereçam ser aproveitados.

Como já mencionamos, normalmente utilizamos algoritmos de aprendizado de máquina para aprender relações que permitam fazer previsões futuras a partir de dados passados. Entretanto, quase sempre partimos do pressuposto de que essas relações existem e podem ser aprendidas a partir dos dados. Raramente dedicamos tempo a comprovar sua existência. Já os traders discricionários, por uma questão de sobrevivência profissional, precisavam identificar relações confiáveis que lhes permitissem manter uma carreira nos mercados. Sob essa perspectiva, podemos argumentar que esses traders realizaram manualmente um trabalho de investigação que pode servir de base para o desenvolvimento de nossos modelos de aprendizado de máquina.

Por isso, buscamos aproximar o aprendizado de máquina do trading nos mercados financeiros, aproveitando as heurísticas e regras empíricas desenvolvidas pelos traders ao longo de muitos anos de experiência. Essas regras, que os traders aprenderam a seguir na prática, podem fornecer uma base para que nossos modelos estudem os mercados financeiros de maneira mais estruturada. Em vez de exigir que os modelos comprovem essas relações desde o início, podemos partir de princípios que já resistiram ao teste do tempo. Dessa forma, nossos modelos passam a contar com uma vantagem inicial, sem precisar construir todo esse conhecimento do zero.

O aspecto mais importante do nosso teste é comprovar a validade da estratégia que adotamos como ponto de partida. Por isso, nossa estratégia de trading se baseia em um conhecido método de rompimento que considera a relação entre dias de negociação consecutivos. Na prática, trata-se de uma estratégia bastante simples.


Visão geral da nossa estratégia de trading

No início de cada pregão, identificamos a máxima e a mínima do dia anterior.

Figura 1: Configuração da nossa estratégia de rompimento diário por meio da marcação dos níveis de preço máximo e mínimo do dia anterior

Quando o preço rompe a máxima do dia anterior, abrimos uma posição vendida e realizamos o lucro na mínima daquele dia. Por outro lado, quando o preço rompe a mínima do dia anterior, abrimos uma posição comprada e realizamos o lucro na máxima do dia anterior. Trata-se, portanto, de uma estratégia contratendência que busca uma única operação de rompimento por dia.

Figura 2: Nossos sinais de entrada são acionados no primeiro rompimento de um dos níveis extremos de preço do dia anterior

Curiosamente, mesmo sem recorrer à modelagem preditiva ou a redes neurais avançadas, constatamos que compreender profundamente o funcionamento dos mercados financeiros é extremamente valioso. Em sua forma original, por mais rudimentar que possa parecer, essa estratégia de rompimento diário alcançava taxas de acerto comparáveis às de uma rede neural profunda, apoiando-se apenas na lógica de mercado assimilada pelos traders discricionários.

Figura 3: A estratégia encerra as posições quando ocorre o rompimento do nível extremo de preço oposto, mas, como mostra a ilustração acima, apresenta determinada margem de erro

Também observamos que a estratégia era excessivamente agressiva e instável. As regras de saída definidas pela estratégia nem sempre eram cumpridas integralmente, como mostra a Figura 3. Por isso, foi necessário introduzir ajustes que permitissem controlar melhor a volatilidade. Neste contexto, entendemos por volatilidade as oscilações do lucro na conta durante o backtest.

Como veremos nas próximas seções, primeiro implementamos a estratégia original em sua forma mais simples, conforme a descrição normalmente adotada pelos traders, para estabelecer uma referência de desempenho. Em seguida, introduzimos ajustes e comparamos os resultados com os da versão original, a fim de verificar se as alterações estavam produzindo os efeitos desejados. Esses ajustes tiveram impacto em três aspectos importantes da estratégia:

Lucratividade

Nossa estratégia original gerou apenas US$ 40 de lucro ao longo de cinco anos de backtest. Esse resultado está longe de ser impressionante. O que chama a atenção, porém, é que 68% de todas as operações realizadas foram lucrativas, uma taxa de acerto expressiva para um teste fora da amostra de cinco anos, especialmente considerando que a estratégia não possuía parâmetros ajustáveis e dependia exclusivamente da lógica e da intuição. Infelizmente, o prejuízo médio das operações perdedoras tendia a superar o lucro médio das operações vencedoras, o que explica o resultado final de apenas US$ 40.

Após algumas iterações, nossa versão aprimorada da estratégia gerou US$ 162 de lucro no mesmo período, um aumento de aproximadamente 300%. Além disso, conseguimos elevar o índice de Sharpe da estratégia de 0,13 para 0,40, uma melhoria de 207% em relação à versão original. Vale destacar que todas as operações foram executadas com o tamanho mínimo de lote permitido, pois nosso objetivo era testar o sistema e demonstrar seu desempenho.

Eficiência das operações

A estratégia original precisou de 886 operações para gerar um lucro de apenas US$ 40, enquanto nossa versão aprimorada obteve US$ 162 de lucro com apenas 246 operações. Isso significa que a estratégia aprimorada reduziu em 72% a atividade de negociação e a exposição desnecessária ao risco de mercado, além de gerar um lucro maior. Essa é uma característica desejável em qualquer sistema de trading: obter mais lucro com menos risco.

Na estratégia original, o lucro médio por operação vencedora era de US$ 3,64, enquanto o prejuízo médio por operação perdedora chegava a US$ 7,87. Conseguimos corrigir esse desequilíbrio. Na versão aprimorada, elevamos o lucro médio para US$ 14,82, enquanto o prejuízo médio permaneceu praticamente inalterado, em US$ 7,95.

Taxa de acerto das operações

Infelizmente, embora essas alterações tenham melhorado a lucratividade e a eficiência da estratégia, também prejudicaram sua taxa de acerto. A proporção de operações lucrativas caiu de 68% na estratégia original para 37% na versão aprimorada, uma redução relativa de aproximadamente 45%. Uma das características mais atraentes da estratégia original era justamente sua elevada taxa de acerto, e aperfeiçoá-la manualmente sem comprometer esse resultado mostrou-se uma tarefa difícil.

Por isso, podemos considerar a possibilidade de delegar essa tarefa a um controlador com realimentação, conforme discutimos anteriormente (link disponível aqui). Também acreditamos que seria interessante continuar testando outras regras de trading discricionário utilizadas pelos traders há muitos anos, selecionando aquelas que naturalmente apresentam taxas de acerto elevadas, como a que observamos inicialmente. Dessa forma, essas estratégias podem oferecer uma base estruturada para que nossos modelos de aprendizado de máquina estudem tanto os pontos fortes quanto as limitações da intuição humana. Vamos começar!


Começando com MQL5

Como fazemos na maioria de nossas estratégias, começaremos definindo as variáveis globais necessárias para executar a estratégia escolhida. Precisaremos de identificadores para os dois níveis de preço que vamos monitorar: a máxima e a mínima do dia anterior. Também marcaremos esses níveis com linhas horizontais que se estendem pelo gráfico. Para isso, precisaremos de variáveis do tipo string para armazenar os nomes desses níveis.

//+------------------------------------------------------------------+
//|                                                         UB 2.mq5 |
//|                                               Gamuchirai Ndawana |
//|                    https://www.mql5.com/ru/users/gamuchiraindawa |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Ndawana"
#property link      "https://www.mql5.com/ru/users/gamuchiraindawa"
#property version   "1.00"

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
double last_high,last_low;
string h,l;
bool rest;
int atr_handler;
double atr[];

Além disso, importaremos a biblioteca de negociação que nos ajudará a gerenciar nossas posições durante o backtest da estratégia.

//+------------------------------------------------------------------+
//| Libraries                                                        |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
CTrade Trade;

Na primeira inicialização do sistema, armazenaremos os nomes dos dois níveis de preço monitorados, configuraremos o indicador Average True Range (ATR) para a gestão de risco e, por fim, reinicializaremos a flag do sistema chamada "rest". Como o nome sugere, nosso sistema continuará procurando oportunidades de negociação enquanto a flag rest não estiver definida como true. Quando isso acontecer, o sistema deixará de procurar novos setups de entrada e passará a gerenciar as posições abertas.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   h="high";
   l="low";
   rest = false;
   atr_handler = iATR(Symbol(),PERIOD_H8,4*14);
//---
   return(INIT_SUCCEEDED);
  }

Quando nosso sistema deixar de ser utilizado, liberaremos o indicador ATR e os recursos associados, devolvendo a memória ao sistema.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   IndicatorRelease(atr_handler);
  }

No início de cada pregão, registraremos a máxima e a mínima do dia anterior.

//---
   datetime cd = iTime(Symbol(),PERIOD_D1,0);
   static datetime ds;

   if(cd != ds)
     {
      ds = cd;
      last_high = iHigh(Symbol(),PERIOD_D1,1);
      last_low = iLow(Symbol(),PERIOD_D1,1);

      if((rest==true) && (PositionsTotal() == 0))
         rest = false;

      Comment("Last High: ",last_high,"\nLast Low: ",last_low);

      ObjectDelete(0,l);
      ObjectDelete(0,h);

      ObjectCreate(0,h,OBJ_HLINE,0,0,last_high);
      ObjectCreate(0,l,OBJ_HLINE,0,0,last_low);
      CopyBuffer(atr_handler,0,0,1,atr);
     }

Durante o pregão, abriremos posições contratendência assim que o preço romper o primeiro desses dois níveis extremos. Consideramos que esse primeiro rompimento corresponde à verdadeira tendência do dia.

   datetime ch = iTime(Symbol(),PERIOD_H1,0);
   static datetime hs;

   if(ch != hs)
     {
      hs = ch;
      double bid,ask,close,padding;

      close = iClose(Symbol(),PERIOD_H1,0);
      ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
      bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);
      padding = atr[0] * 2;

      if(rest == false)
        {
         if(PositionsTotal() == 0)
           {

            if(close<last_low)
               Trade.Buy(0.01,Symbol(),ask,(bid-(padding)),last_high);
               
            else
               if(close>last_high)
                  Trade.Sell(0.01,Symbol(),bid,(ask+(padding)),last_low);
                  
            rest = true;
           }
        }
     }
  }
//+------------------------------------------------------------------+

Como a estratégia se baseia essencialmente na intuição, sua estrutura é bastante simples. Concluímos todas as configurações necessárias e agora podemos começar a testar o sistema de trading com dados históricos.

Começaremos selecionando cinco anos de dados históricos do par EUR/USD para avaliar o desempenho do nosso sistema.

Figura 4: Estabelecimento de uma referência de desempenho com a versão original da nossa estratégia de trading

Em seguida, ajustaremos as configurações de atraso para utilizar atrasos aleatórios, reproduzindo a imprevisibilidade das condições reais de negociação.

Figura 5: Utilização de atrasos aleatórios para garantir condições de simulação mais realistas

Ao observarmos a evolução do patrimônio líquido da conta ao longo do tempo, percebemos que, apesar de bastante simples e rudimentar, a estratégia apresenta uma tendência de crescimento. No entanto, a amplitude das oscilações entre os picos e os vales é excessiva, indicando a necessidade de medidas que tornem a estratégia menos agressiva e permitam controlar melhor seus resultados.

Figura 6: A curva de patrimônio líquido da versão original da nossa estratégia de trading apresenta resultados inconsistentes e oscilações excessivas

Ainda assim, o lucro total gerado pela estratégia em sua forma original foi de apenas US$ 40 ao longo de cinco anos de backtest. O que realmente impressiona, porém, é sua elevada taxa de acerto: 68% de todas as operações realizadas foram lucrativas. O problema era que o lucro médio das operações vencedoras ficava abaixo do prejuízo médio das operações perdedoras, com perdas quase duas vezes maiores que os ganhos.

Figura 7: As estatísticas detalhadas da nossa estratégia original revelam uma taxa de acerto naturalmente elevada


Implementando melhorias

Para aprimorar a estratégia original, precisamos buscar alternativas na forma como ela analisa o mercado, especialmente maneiras de antecipar os movimentos de preço sem precisar esperar, ao longo do pregão, pelo rompimento de um dos níveis estabelecidos. Depois de refletir bastante sobre essa questão, percebi que poderíamos comparar, durante a primeira hora de cada pregão, a distância entre o preço de abertura do dia e os níveis extremos do dia anterior.

Dessa forma, conseguimos medir a distância entre o preço de abertura do novo pregão e a máxima e a mínima do dia anterior. Nossa intuição sugere que o preço tende a se movimentar na direção em que há mais espaço disponível. Portanto, se o pregão começar com o preço mais próximo da mínima do dia anterior, abriremos posições compradas. Por outro lado, se começar mais próximo da máxima do dia anterior, abriremos posições vendidas.

Figura 8: Visualização das melhorias que podemos implementar na versão original da nossa estratégia de trading

A implementação dessa melhoria permitiu aumentar nosso lucro líquido total em relação aos US$ 40 obtidos inicialmente. Com isso, passamos a antecipar os movimentos de preço durante o pregão, sem precisar aguardar o cumprimento das regras tradicionais da estratégia. Para isso, bastaram pequenos ajustes nas regras de entrada: passamos a considerar a distância absoluta entre o preço de fechamento atual e a máxima e a mínima do dia anterior. Se a distância até a máxima fosse maior, abríamos posições compradas. Caso contrário, abríamos posições vendidas. Após abrir cada posição, ativávamos a flag de controle "rest". A partir desse momento, só voltávamos a procurar novas oportunidades de negociação depois que a operação em andamento fosse encerrada.

      if(rest == false)
        {
         if(PositionsTotal() == 0)
           {
            //High Gap Is Bigger
            if(MathAbs(close - last_high) > MathAbs(close - last_low))
               Trade.Buy(0.01,Symbol(),ask,(bid-(padding)),last_high);
	       rest = true;

            
            //Low Gap Is Bigger
            else
               if(MathAbs(close - last_high) < MathAbs(close - last_low))
                  Trade.Sell(0.01,Symbol(),bid,(ask+(padding)),last_low);
	          rest = true;
           }
        }

Depois de integrar essas alterações, nosso novo sistema ficou assim:

//+------------------------------------------------------------------+
//|                                                         UB 2.mq5 |
//|                                               Gamuchirai Ndawana |
//|                    https://www.mql5.com/ru/users/gamuchiraindawa |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Ndawana"
#property link      "https://www.mql5.com/ru/users/gamuchiraindawa"
#property version   "1.00"

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
double last_high,last_low;
string h,l;
bool rest;
int atr_handler;
double atr[];

//+------------------------------------------------------------------+
//| Libraries                                                        |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
CTrade Trade;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   h="high";
   l="low";
   rest = false;
   atr_handler = iATR(Symbol(),PERIOD_H8,4*14);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   IndicatorRelease(atr_handler);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   datetime cd = iTime(Symbol(),PERIOD_D1,0);
   static datetime ds;

   if(cd != ds)
     {
      ds = cd;
      last_high = iHigh(Symbol(),PERIOD_D1,1);
      last_low = iLow(Symbol(),PERIOD_D1,1);

      if((rest==true) && (PositionsTotal() == 0))
         rest = false;


      Comment("Last High: ",last_high,"\nLast Low: ",last_low);

      ObjectDelete(0,l);
      ObjectDelete(0,h);

      ObjectCreate(0,h,OBJ_HLINE,0,0,last_high);
      ObjectCreate(0,l,OBJ_HLINE,0,0,last_low);
      CopyBuffer(atr_handler,0,0,1,atr);
     }

   datetime ch = iTime(Symbol(),PERIOD_H1,0);
   static datetime hs;

   if(ch != hs)
     {
      hs = ch;
      last_high = iHigh(Symbol(),PERIOD_D1,1);
      last_low = iLow(Symbol(),PERIOD_D1,1);
      double bid,ask,close,padding;

      close = iClose(Symbol(),PERIOD_H1,0);
      ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
      bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);
      padding = atr[0] * 2;

      if(rest == false)
        {
         if(PositionsTotal() == 0)
           {
            if(MathAbs(close - last_high) > MathAbs(close - last_low))
               Trade.Buy(0.01,Symbol(),ask,(bid-(padding)),last_high);

            else
               if(MathAbs(close - last_high) < MathAbs(close - last_low))
                  Trade.Sell(0.01,Symbol(),bid,(ask+(padding)),last_low);

            rest = true;
           }
        }
     }
  }
//+------------------------------------------------------------------+

A curva de patrimônio líquido gerada pela estratégia aprimorada atingiu patamares que a versão original não havia conseguido alcançar. Nosso lucro líquido total passou dos US$ 40 iniciais para US$ 148. No entanto, já podemos observar uma queda na proporção de operações lucrativas.

Figura 9: Visualização da curva de patrimônio líquido gerada pela versão aprimorada da nossa estratégia de trading original

Por um lado, conseguimos melhorar o desempenho da estratégia, aumentando o lucro em US$ 108. Além disso, enquanto a versão original precisou de 886 operações para gerar US$ 40 de lucro, a nova versão alcançou US$ 148 com apenas 469 operações. A estratégia está claramente evoluindo na direção desejada, mas ainda há espaço para novos aprimoramentos.

Figura 10: Análise estatística das melhorias implementadas em relação à versão original da estratégia


Implementando melhorias adicionais

Para entender melhor essa questão, vamos analisar o diagrama abaixo. Representaremos a distância até a máxima com um retângulo verde e a distância até a mínima com um retângulo azul. Até aqui, nossa hipótese é que o preço tende a se movimentar na direção em que existe a maior distância. Atualmente, realizamos o lucro assim que o preço rompe um dos níveis extremos do dia anterior.

Figura 11: Os retângulos verde e azul representam, respectivamente, a distância até a máxima e a mínima do dia anterior

No entanto, como podemos observar, o preço nem sempre consegue ultrapassar esses níveis. Às vezes, ele se aproxima bastante, mas não chega a cruzá-los.

Figura 12: As condições de saída da estratégia original nem sempre são atendidas em todas as condições possíveis de mercado

Portanto, podemos considerar uma mudança: em vez de posicionar nossos níveis de take-profit acima do extremo anterior, poderíamos colocá-los um pouco abaixo desse nível, talvez a uma fração do ATR abaixo da máxima do dia anterior. Dessa forma, mesmo que o preço apenas se aproxime suficientemente do nível desejado, ainda conseguiremos realizar o lucro.

Além disso, queremos tornar os níveis de stop-loss progressivamente mais restritivos à medida que as operações evoluem a nosso favor, buscando maximizar os lucros. Para implementar essa melhoria, modificamos nossas regras: em vez de aplicar o mesmo afastamento ao stop-loss e ao take-profit, passamos a utilizar uma fração menor para o take-profit do que para o stop-loss. Com isso, o stop-loss fica com uma margem maior, enquanto o take-profit permanece mais próximo, permitindo aproveitar o impulso do movimento de preço.

Enquanto as posições permanecem abertas, também verificamos se o novo nível de stop-loss proposto é mais favorável do que o atual. O ideal é manter o stop-loss vinculado ao extremo de preço mais recente, sempre que possível.

   if(ch != hs)
     {
      hs = ch;
      last_high = iHigh(Symbol(),PERIOD_D1,1);
      last_low = iLow(Symbol(),PERIOD_D1,1);
      double bid,ask,close,padding;

      close = iClose(Symbol(),PERIOD_H1,0);
      ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
      bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);
      padding = atr[0] * 2;

      if(rest == false)
        {
         if(PositionsTotal() == 0)
           {

            if((MathAbs(close - last_high) > MathAbs(close - last_low)))
               Trade.Buy(0.01,Symbol(),ask,(bid-(padding)),last_high+(padding*0.9));

            else
               if((MathAbs(close - last_high) < MathAbs(close - last_low)))
                  Trade.Sell(0.01,Symbol(),bid,(ask+(padding)),last_low-(padding*0.9));

            rest = true;
           }
        }

      if(rest == true)
        {
         if(PositionsTotal() > 0)
           {
            if(PositionSelectByTicket(PositionGetTicket(0)))
              {
               double sl,tp;

               sl = PositionGetDouble(POSITION_SL);
               tp = PositionGetDouble(POSITION_TP);

               //--- Buy
               if(sl < tp)
                 {
                  if(last_low > sl)
                     Trade.PositionModify(Symbol(),last_low,tp);
                 }

               if(tp < sl)
                 {
                  if(last_high < sl)
                     Trade.PositionModify(Symbol(),last_high,tp);
                 }
              }
           }
        }
     }

Com essas alterações, chegamos à versão mais recente do nosso sistema:

//+------------------------------------------------------------------+
//|                                                         UB 2.mq5 |
//|                                               Gamuchirai Ndawana |
//|                    https://www.mql5.com/ru/users/gamuchiraindawa |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Ndawana"
#property link      "https://www.mql5.com/ru/users/gamuchiraindawa"
#property version   "1.00"

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
double last_high,last_low;
string h,l;
bool rest;
int atr_handler;
double atr[];

//+------------------------------------------------------------------+
//| Libraries                                                        |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
CTrade Trade;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   h="high";
   l="low";
   rest = false;
   atr_handler = iATR(Symbol(),PERIOD_H8,4*14);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
      IndicatorRelease(atr_handler);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   datetime cd = iTime(Symbol(),PERIOD_D1,0);
   static datetime ds;

   if(cd != ds)
     {
      ds = cd;
      last_high = iHigh(Symbol(),PERIOD_D1,1);
      last_low = iLow(Symbol(),PERIOD_D1,1);

      if((rest==true) && (PositionsTotal() == 0))
         rest = false;

      Comment("Last High: ",last_high,"\nLast Low: ",last_low);

      ObjectDelete(0,l);
      ObjectDelete(0,h);

      ObjectCreate(0,h,OBJ_HLINE,0,0,last_high);
      ObjectCreate(0,l,OBJ_HLINE,0,0,last_low);
      CopyBuffer(atr_handler,0,0,1,atr);
     }

   datetime ch = iTime(Symbol(),PERIOD_H1,0);
   static datetime hs;

   if(ch != hs)
     {
      hs = ch;
      last_high = iHigh(Symbol(),PERIOD_D1,1);
      last_low = iLow(Symbol(),PERIOD_D1,1);
      double bid,ask,close,padding;

      close = iClose(Symbol(),PERIOD_H1,0);
      ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
      bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);
      padding = atr[0] * 2;

      if(rest == false)
        {
         if(PositionsTotal() == 0)
           {

            if((MathAbs(close - last_high) > MathAbs(close - last_low)))
               Trade.Buy(0.01,Symbol(),ask,(bid-(padding)),last_high+(padding*0.9));

            else
               if((MathAbs(close - last_high) < MathAbs(close - last_low)))
                  Trade.Sell(0.01,Symbol(),bid,(ask+(padding)),last_low-(padding*0.9));

            rest = true;
           }
        }

      if(rest == true)
        {
         if(PositionsTotal() > 0)
           {
            if(PositionSelectByTicket(PositionGetTicket(0)))
              {
               double sl,tp;

               sl = PositionGetDouble(POSITION_SL);
               tp = PositionGetDouble(POSITION_TP);

               //--- Buy
               if(sl < tp)
                 {
                  if(last_low > sl)
                     Trade.PositionModify(Symbol(),last_low,tp);
                 }

               if(tp < sl)
                 {
                  if(last_high < sl)
                     Trade.PositionModify(Symbol(),last_high,tp);
                 }
              }
           }
        }
     }
  }

A curva de patrimônio líquido agora alcança níveis superiores aos de todas as versões anteriores da estratégia. A volatilidade, entendida aqui como a amplitude dos resultados entre operações lucrativas e deficitárias, também está mais controlada.

Figura 13: A versão final da nossa estratégia de trading apresenta uma curva de patrimônio líquido com a estrutura desejada, que não estava presente na versão original

Ao examinar as estatísticas detalhadas, constatamos que o lucro líquido total voltou a aumentar, passando de US$ 148 na versão anterior para US$ 162 nesta última. Entretanto, a proporção de operações lucrativas voltou a cair, ficando abaixo tanto da versão anterior quanto da estratégia original. Isso mostra que nossos aprimoramentos não afetam apenas as operações vencedoras, mas também acabam afetando as perdedoras de maneira indesejada.

Isso evidencia um problema mais profundo: aperfeiçoar estratégias de trading desenvolvidas exclusivamente com base na intuição humana não é uma tarefa que se resolva facilmente recorrendo a mais intuição. Para avançarmos de fato, precisamos considerar outras formas de aprimoramento que não dependam do julgamento humano.

Figura 14: Uma análise mais detalhada revela que nossas alterações também prejudicaram o desempenho do sistema em termos de taxa de acerto


Conclusão

Os traders não conseguiram se manter nos mercados financeiros por mero acaso, nem construíram suas carreiras operando às cegas, de uma negociação para outra. A necessidade de obter resultados e o receio de sofrer perdas os levaram a buscar relações reais e confiáveis no comportamento do mercado. Hoje, essa disciplina muitas vezes é deixada de lado, enquanto os especialistas em aprendizado de máquina procuram volumes cada vez maiores de dados e modelos mais complexos, sem questionar se suas métricas realmente representam avanços significativos.

Nosso trabalho aponta para um caminho diferente. Ao fundamentarmos nossas estratégias na lógica de mercado desenvolvida pelos traders discricionários, partimos de princípios cuja validade já foi demonstrada, em vez de pressupor sua existência sem qualquer comprovação. Os erros residuais dessas estratégias, ou seja, as imperfeições que ainda persistem nessas estratégias, tornam-se uma fonte valiosa de aprendizado para os modelos de aprendizado de máquina. Dessa forma, deixamos de exigir que nossos algoritmos comprovem a existência dessas relações desde o início. Em vez disso, oferecemos uma estrutura na qual as entradas e as saídas já apresentam relações estabelecidas.

Essa mudança também nos aproxima do atendimento aos pressupostos estatísticos dos quais nossos modelos dependem, muitas vezes de maneira implícita. Uma estratégia fixa, aplicada a um mercado em constante mudança, gera resultados que se aproximam do comportamento de observações independentes e identicamente distribuídas, condições necessárias para um aprendizado robusto. No mundo real, talvez nunca seja possível obter um conjunto perfeito de variáveis aleatórias independentes e identicamente distribuídas, mas essa abordagem nos aproxima desse ideal, tornando nossos modelos menos vulneráveis e mais confiáveis. Acreditamos que, ao partir da lógica de mercado adquirida pela experiência humana e posteriormente treinar os modelos para aprender com as falhas dessa lógica, podemos oferecer aos nossos modelos de aprendizado de máquina um ponto de partida mais avançado.


Nome do arquivo Descrição do arquivo
Daily Breakout Strategy V1.mq5 Versão original e agressiva da nossa estratégia de trading, que apresentou uma elevada taxa de acerto nos testes iniciais.
Daily Breakout Strategy V2.mq5 Segunda versão do nosso sistema de trading, que apresentou uma taxa de acerto um pouco menor, mas uma lucratividade significativamente superior.
Daily Breakout Strategy V3.mq5 Versão final da nossa estratégia de trading, que apresentou a maior lucratividade, mas também a menor taxa de acerto.

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

Recursos do Assistente MQL5 que você precisa conhecer (Parte 79): Uso dos osciladores Gator e A/D no aprendizado supervisionado Recursos do Assistente MQL5 que você precisa conhecer (Parte 79): Uso dos osciladores Gator e A/D no aprendizado supervisionado
No artigo anterior, concluímos a análise da combinação dos osciladores Gator e Acumulação/Distribuição (A/D) em sua aplicação convencional, baseada nos sinais brutos gerados por esses indicadores. Eles se complementam: o Gator atua como indicador de tendência, enquanto o oscilador Acumulação/Distribuição funciona como indicador de volume. Agora, daremos continuidade ao tema, investigando como o aprendizado supervisionado pode melhorar alguns dos padrões de sinal estudados anteriormente. Nossa abordagem utiliza uma rede neural convolucional (CNN), na qual a regressão por kernel e a similaridade baseada no produto escalar são empregadas para definir os tamanhos dos kernels e a quantidade de canais. Como de costume, implementaremos essa abordagem em um arquivo personalizado da classe de sinal, que interage com o Assistente MQL5 durante a criação do EA.
Desenvolvimento de ferramentas para análise de Price Action (Parte 37): Indicador de viés do sentimento de mercado Desenvolvimento de ferramentas para análise de Price Action (Parte 37): Indicador de viés do sentimento de mercado
O sentimento de mercado é uma das forças mais subestimadas e, ao mesmo tempo, mais influentes sobre o movimento dos preços. Enquanto a maioria dos traders depende de indicadores defasados ou de suposições, o EA Sentiment Tilt Meter (STM) transforma os dados de mercado em uma referência visual clara, mostrando em tempo real se o mercado apresenta um viés de alta, de baixa ou permanece neutro. Isso facilita a confirmação das operações, ajuda a evitar entradas em falso e permite escolher com mais precisão o momento de entrar no mercado.
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Operações CRUD no Firebase usando MQL Operações CRUD no Firebase usando MQL
Este artigo apresenta um guia passo a passo para dominar as operações CRUD (Create, Read, Update, Delete, ou criação, leitura, atualização e exclusão) no Firebase, com foco no Realtime Database e no Firestore. Você aprenderá a utilizar os métodos do SDK do Firebase para gerenciar dados de forma eficiente em aplicações web e móveis, desde a criação de novos registros até sua consulta, atualização e exclusão. O artigo também apresenta exemplos práticos de código e boas práticas para estruturar e processar dados em tempo real, ajudando os desenvolvedores a criar aplicações dinâmicas e escaláveis com a arquitetura NoSQL flexível do Firebase.