preview
Desenvolvendo uma Estratégia de Pullback: Construção do Modelo Base

Desenvolvendo uma Estratégia de Pullback: Construção do Modelo Base

MetaTrader 5Negociação |
62 0
Samuel De Souza Ferreira
Samuel De Souza Ferreira

Introdução

Após a construção inicial da lógica de pullback, a etapa seguinte foi transformar a ideia em uma estrutura testável. Para isso, analise como cada parte da estratégia influenciava o comportamento estatístico do modelo. O objetivo não foi apenas otimizar parâmetros, mas compreender se havia uma organização lógica capaz de gerar um primeiro indício de robustez.

A estratégia parte de uma estrutura simples: utilizar uma EMA para definir o regime, identificar uma retração em um timeframe menor e aguardar um cruzamento de retomada como gatilho de entrada. A partir desse modelo base, foram analisados os principais graus de liberdade da estratégia, como timeframe e período da EMA, formando 4 graus de liberdade.

Neste artigo, esses parâmetros são analisados de forma progressiva, utilizando os recursos de otimização do MetaTrader 5, como gráficos de resultados, análise 1D e em 2D. A intenção é observar regiões de parâmetros mais estáveis, evitando escolher apenas o melhor resultado isolado da otimização.

Também será exibido quando faz sentido expandir ou ajustar os intervalos de otimização. Isso é útil quando os resultados aparecem nas bordas dos intervalos ou quando há forte sensibilidade entre melhores configurações próximas. Dessa forma, a estratégia começa a ser construída em camadas, com maior controle sobre cada decisão adicionada ao EA.


Núcleo de estabilidade com separação de timeframes

O primeiro avanço relevante ocorreu ao separar melhor as funções da estratégia. Em vez de tentar usar timeframes muito curtos para tudo, a otimização indicou que o modelo passava a se comportar melhor quando o regime era definido em um timeframe mais lento e a retração/gatilho ficava em um timeframe mais rápido.

As características iniciais encontradas foram:

  • Regime: M15
  • Retração e gatilho: M1
  • EMA do regime: período 75/80
  • EMA da retração: período 40
  • Gatilho: cruzamento fechado de retomada da EMA de retração

Essa combinação começou a apresentar lucro positivo onde leituras mais curtas falhavam. O melhor resultado inicial apareceu com regimeTimeframeIndex = 5, retracementTimeframeIndex = 0, regimeEMAPeriod próximo de 75/80 e retracementEMAPeriod = 40.

O resultado ainda não era forte o suficiente para ser considerado uma estratégia final, mas já indicava um comportamento estatístico relevante: cerca de 1961 operações, fator de lucro próximo de 1.05 e drawdown em torno de 19%. O principal valor dessa etapa foi revelar que a arquitetura M15/M1 parecia mais importante do que o valor isolado das EMAs.

Initial backtest

Figure 1- Initial Backtest

Ao observar o gráfico de resultados da otimização abaixo, também fica claro que o modelo ainda era frágil. Apesar de existirem alguns pontos superiores com saldo máximo mais interessante, a maior parte das passagens permanece concentrada em regiões ruins ou medianas, com muitos resultados em prejuízo e grande dispersão entre os testes. Isso indica que a lógica inicial ainda não estava consolidada em uma região ampla de estabilidade.

Essa leitura é importante porque, mesmo antes de utilizar um OnTester mais criterioso, combinando lucro, fator de lucro, payoff e drawdown em um score próprio, o gráfico já mostrava que não bastava escolher o melhor resultado isolado. Era necessário investigar se os parâmetros vencedores faziam parte de um platô ou se eram apenas pontos específicos dentro de uma otimização ainda instável.

Distribution of the Initial Optimization

Figure 2- Distribution of the Initial Optimization


Verificação do extremo da EMA de regime

Como os melhores resultados iniciais ficaram próximos do limite superior da faixa otimizada, entre 75 e 80 períodos, não seria correto aceitar diretamente esse valor como definitivo. Quando um parâmetro aparece no extremo da otimização, existe o risco de a faixa testada ter sido curta demais.

Por isso, foi necessário expandir a busca para verificar se havia continuidade, platô ou apenas um encaixe de borda.

Abaixo é mostrado à esquerda uma otimização em que há necessidade de ampliar os limites devido aos maiores resultados se acumularem nos extremos, e à direita a otimização conseguiu convergir o pico dentro dos limites, sem mostrar nenhum ponto isolado em suas vizinhanças com uma possível região paramétrica mais consistente.

Boundary result versus expanded parameter range

Figure 3- Boundary result versus expanded parameter range

Ao expandir a análise, valores como 85 e até regiões próximas de 100 ainda mostraram comportamento interessante. Isso indicou que o regime precisava ser mais lento e estrutural, reduzindo a sensibilidade a ruídos de curto prazo. Após essa expansão, o valor 85 foi escolhido como referência inicial, pois concentrou os melhores resultados dentro dessa nova faixa analisada.

A interpretação metodológica foi:

  • Se o melhor parâmetro está no extremo da faixa, a faixa deve ser expandida.
  • Se valores próximos continuam funcionando, há indício de platô.
  • Se apenas o valor extremo funciona, o risco de overfitting aumenta.
  • Se valores maiores mantêm ou melhoram o comportamento, o modelo pode estar pedindo um regime mais lento.


Implementação do stop por tick size

Antes da inclusão dos filtros adicionais no gatilho, foi necessário estruturar o stop loss inicial do EA. Até esse ponto, o input de stop havia sido mantido como 0, permitindo que a estratégia fosse analisada sem stop loss, apenas com saídas pela lógica do modelo. Em seguida, o EA passou a utilizar um stop baseado em ticks do próprio mercado, calculado a partir do SYMBOL_TRADE_TICK_SIZE. Essa escolha facilita a análise e o controle do risco, pois o stop deixa de depender diretamente de pontos gráficos ou valores monetários e passa a respeitar a menor variação operacional do ativo.

No código, essa lógica foi estruturada a partir de um input simples. Quando stopLossTicks é igual a 0, o EA não envia stop loss. Quando o valor é maior que zero, o stop é calculado multiplicando a quantidade de ticks pelo tick size do ativo.

input int stopLossTicks = 0;    // Stop distance in symbol ticks. Use 0 to disable stop loss.

Durante a inicialização, o tick size é lido diretamente das propriedades do ativo:

//--- Read the symbol tick size.
//--- If the broker does not return a valid tick size, use _Point as fallback.
g_tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);

if(g_tickSize <= 0.0)
   g_tickSize = _Point;

A função responsável por calcular o preço do stop verifica primeiro se o stop está ativado. Se stopLossTicks for igual a 0, a função retorna 0.0, e o EA envia a ordem sem stop loss. Caso contrário, o stop é calculado a partir do preço de entrada e da distância em ticks.

//+------------------------------------------------------------------+
//| Initial stop loss price                                          |
//+------------------------------------------------------------------+
double InitialStopLossPrice(const ENUM_ORDER_TYPE orderType,
                            const double entryPrice)
  {
//--- If stopLossTicks is zero, the EA does not use an initial stop loss.
   if(stopLossTicks <= 0 || entryPrice <= 0.0 || g_tickSize <= 0.0)
      return 0.0;

//--- Convert the stop distance from ticks into price distance.
   double stopDistance = (double)stopLossTicks * g_tickSize;

//--- Buy stop loss:
//--- the stop is placed below the estimated entry price.
   if(orderType == ORDER_TYPE_BUY)
      return NormalizeDouble(entryPrice - stopDistance, _Digits);

//--- Sell stop loss:
//--- the stop is placed above the estimated entry price.
   if(orderType == ORDER_TYPE_SELL)
      return NormalizeDouble(entryPrice + stopDistance, _Digits);

   return 0.0;
  }

Na abertura da posição, o EA calcula o stop antes de enviar a ordem. Se o valor retornado for 0.0, a ordem segue sem stop loss. Se houver um valor válido, ele é enviado no campo request.sl.

//--- Estimate the entry price according to the order direction.
double entryPrice = (isBuy ? g_ask : g_bid);
ENUM_ORDER_TYPE orderType = (isBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL);

//--- Calculate the initial stop loss price.
//--- If stopLossTicks is zero, stopLossPrice will also be zero.
double stopLossPrice = InitialStopLossPrice(orderType, entryPrice);
//--- No envio da ordem, o stop calculado é aplicado diretamente:

//--- Send the stop loss price to the trade request.
//--- When stopLossPrice is 0.0, the order is sent without stop loss.
request.sl = stopLossPrice;
request.tp = 0.0;
Para avaliar o impacto dessa nova camada de risco, foi realizada uma otimização do parâmetro stopLossTicks, com a seguinte faixa:

  • Stop mínimo: 10 ticks
  • Stop máximo: 200 ticks
  • Passo: 10 ticks
  • Melhor valor observado no momento: 110 ticks

    O resultado mostrou que a adição do stop não quebrou a lógica inicial da estratégia. Pelo contrário, mesmo com a limitação de risco por operação, o modelo conseguiu manter lucro positivo, payoff interessante e drawdown ainda controlado. Esse comportamento foi importante porque indicou que a estrutura não dependia exclusivamente de operações sem proteção ou de uma saída muito distante. A região próxima de 110 ticks passou a ser um primeiro candidato operacional, mostrando um cenário em que o stop começa a controlar o risco sem destruir completamente o comportamento do pullback.

    Stop Optimization

    Figure 4- Stop Optimization

    Como o stop loss faz parte da gestão da operação, ele passa a ter uma sensibilidade maior ao modelo de execução utilizado no teste. Embora o trabalho com velas internas, o stop é acionado por preço. Por isso, pode ser afetado por movimentos intrabar, spread, slippage e por diferenças entre OHLC e ticks reais. Por esse motivo, será realizada uma nova otimização mais refinada, agora com o intervalo concentrado entre 110 e 140 ticks, com passo de 10, utilizando o modo cada tick. Essa etapa permitirá verificar se a região encontrada no teste OHLC continua válida quando o stop passa a ser avaliado em uma simulação mais próxima da execução real.

    StopLossTicks Lucro OHLC Lucro cada tick Payoff OHLC Payoff cada tick DD OHLC DD cada tick trades
    110 5610 4347 2.63 2.04 26.55% 20.50% 2131
    120 5009 3925 2.40 1.88 27.07% 28.27% 2090
    130 4675 3755 2.28 1.83 19.94% 21.56% 2050
    140 5003 4148 2.49 2.06 27.42% 28.29% 2013

    Table 1- Stop-loss results in OHLC and Every Tick

    A comparação mostrou que, ao passar de OHLC para Cada Tick, houve redução natural no lucro e no payoff, o que era esperado por se tratar de uma simulação mais sensível à execução real do stop. Mesmo assim, o valor de 110 ticks continuou apresentando o melhor equilíbrio geral. Ele manteve o maior lucro no teste Cada Tick, teve o maior número de operações, payoff competitivo e o menor drawdown entre os principais candidatos analisados nessa faixa.


    Filtro de distância por ATR no gatilho

    Após a etapa do stop, foi adicionado um filtro ao gatilho de entrada. A lógica deixou de considerar apenas o cruzamento da EMA de retração e passou a exigir que o preço fechasse além dela por uma distância mínima baseada em ATR.

    A regra ficou assim:

    • Para compra, o preço precisa cruzar a EMA de retração e fechar acima dela por uma distância mínima em ATR.
    • Para venda, o preço precisa cruzar a EMA de retração e fechar abaixo dela por uma distância mínima em ATR.

      A ideia é evitar entradas em cruzamentos muito fracos, quando o preço passa pela média por poucos pontos, mas ainda permanece muito próximo da EMA. Esse tipo de sinal pode representar apenas ruído ao redor da média, e não necessariamente uma retomada real do movimento.

      No código, a validação principal foi concentrada em uma função simples:

      //+------------------------------------------------------------------+
      //| ATR distance confirmation                                        |
      //+------------------------------------------------------------------+
      bool EntryDistanceFromEMAIsValid(const bool isBuy)
        {
      //--- If the filter is disabled, the EMA cross is accepted directly.
         if(!useEntryDistanceFromEMAByATR)
            return true;
      
      //--- The filter uses only the last fully closed retracement candle.
         double closePrice = g_retracementRates[1].close;
         double emaValue   = g_retracementEMA[1];
         double atrValue   = g_entryATR[1];
      
      //--- ATR must be valid before calculating the required distance.
         if(atrValue <= 0.0)
            return false;
      
      //--- Minimum distance required from the retracement EMA.
         double minimumDistance = atrValue * entryDistanceFromEMAAtr;
      
      //--- Buy confirmation:
      //--- the candle must close above the retracement EMA
      //--- and the distance from the EMA must be at least the ATR threshold.
         if(isBuy)
            return (closePrice > emaValue &&
                    (closePrice - emaValue) >= minimumDistance);
      
      //--- Sell confirmation:
      //--- the candle must close below the retracement EMA
      //--- and the distance from the EMA must be at least the ATR threshold.
         return (closePrice < emaValue &&
                 (emaValue - closePrice) >= minimumDistance);
        }

      A função recebe a direção da operação e verifica se o fechamento do candle ficou suficientemente distante da EMA de retração. Em compras, a distância é calculada entre o fechamento e a EMA abaixo desse fechamento. Em vendas, a lógica é invertida, medindo a distância entre a EMA e o fechamento abaixo dela.

      Essa validação é aplicada diretamente no momento do gatilho:

      //--- Buy setup:
      //--- the pullback must be active,
      //--- price must cross back above the retracement EMA,
      //--- and the close must be far enough from the EMA by ATR.
      if(g_buyRetracementActive &&
         CrossedUpRetracementEMA() &&
         EntryDistanceFromEMAIsValid(true))
        {
         OpenTrade(true);
        }
      
      //--- Sell setup:
      //--- the pullback must be active,
      //--- price must cross back below the retracement EMA,
      //--- and the close must be far enough from the EMA by ATR.
      if(g_sellRetracementActive &&
         CrossedDownRetracementEMA() &&
         EntryDistanceFromEMAIsValid(false))
        {
         OpenTrade(false);
        }
      

      Esse trecho mostra que o EA não entra apenas porque houve o cruzamento da EMA. Para abrir uma posição, três condições precisam acontecer ao mesmo tempo:

      • O pullback precisa estar armado;
      • O preço precisa cruzar a EMA de retração no sentido da retomada;
      • O fechamento precisa respeitar a distância mínima em ATR.

      Se o preço cruzar a EMA, mas não atingir a distância mínima exigida pelo ATR, o sinal daquele cruzamento é descartado. O EA não fica esperando os próximos candles apenas completarem a distância mínima. Como a função CrossedUpRetracementEMA() ou CrossedDownRetracementEMA() depende do cruzamento entre o candle anterior e o candle atual, aquele gatilho deixa de existir após o candle fechado.

      Na prática, isso significa que, mesmo que o pullback tenha ocorrido corretamente, a entrada só será feita se a retomada vier com força mínima suficiente. Caso contrário, o cruzamento desaparece e será necessário um novo cruzamento da EMA de retração para que o EA tenha outra oportunidade de entrada.

      Dessa forma, o ATR não substitui o gatilho principal. O cruzamento da EMA continua sendo necessário, mas passa a ser aceito apenas quando o fechamento demonstra uma retomada mais clara. O objetivo desse filtro é separar cruzamentos fracos, muito próximos da média, de sinais com maior confirmação.

      ATR Optimization

      Figure 5- ATR Optimization

      Os resultados mostraram uma mudança importante no comportamento da estratégia. A frequência de operações foi reduzida de forma significativa, pois muitos cruzamentos fracos passaram a ser descartados. Mesmo assim, houve melhora no lucro líquido e no payoff, acompanhada de uma redução relevante no drawdown. Isso sugere que o filtro conseguiu eliminar parte dos falsos positivos e também pullbacks em que o gatilho ocorria de forma muito leve, sem confirmação suficiente de retomada.

      Entre os melhores resultados, os valores escolhidos foram:

      • entryATRPeriod = 14
      • entryDistanceFromEMAAtr = 1.0

        A escolha desses parâmetros não foi baseada apenas no maior lucro isolado. O conjunto com ATR 14 e distância 1,0 apresentou lucro e payoff consideráveis, mas com drawdown menor em relação a outras combinações próximas. Esse ponto é importante porque o ativo testado possui alavancagem elevada em relação ao saldo inicial, tornando o controle do drawdown tão relevante quanto o aumento do lucro. Portanto, o filtro por ATR passou a cumprir uma função essencial na estratégia: reduzir a quantidade de sinais, mas preservar entradas com melhor qualidade estatística e melhor equilíbrio entre retorno e risco.


        Comparação após implementação de filtro

        Após a adoção do filtro de distância mínima em ATR para confirmar a retomada após o cruzamento, foi realizada uma nova otimização com o stop loss temporariamente desativado. O objetivo foi isolar o efeito do filtro e comparar as duas versões sob condições equivalentes. Nessa etapa, foram reavaliados os parâmetros de regime e retração, mantendo os mesmos timeframes e períodos de EMA utilizados anteriormente.

        A distribuição dos resultados mostrou um cenário de maior estabilidade paramétrica. Diferentemente da otimização inicial, os melhores resultados passaram a aparecer de forma mais distribuída e coerente, sem depender de um único ponto isolado. Isso sugere que o filtro ajudou a reduzir sinais fracos e a diminuir a sensibilidade do modelo a combinações específicas de parâmetros.

        A principal conclusão dessa etapa é que o filtro por ATR fortaleceu a lógica original do pullback. Ele não alterou a estrutura da estratégia, mas passou a selecionar melhor os cruzamentos que apresentavam uma retomada mais consistente do movimento.

        A figura abaixo apresenta, à esquerda, os resultados sem o filtro ATR e, à direita, os resultados após sua implementação.

        Results before and after ATR filter

        Figure 6- Results before and after the ATR filter


        Introdução à gestão

        Até esta etapa, o modelo passou a trabalhar com 7 graus de liberdade principais: timeframe do regime, timeframe da retração/gatilho, período da EMA de regime, período da EMA de retração, stop em tick size, período do ATR do gatilho e distância mínima do preço em relação à EMA medida em ATR. Com essa base definida, a próxima camada do desenvolvimento será a gestão da operação. Para essa análise, a gestão não será construída diretamente em proporção de ATR, mas em RR (risco relativo), ou seja, em múltiplos do risco inicial definido pelo stop. Dessa forma, breakeven, take profit e trailing passam a ser avaliados de maneira proporcional ao risco real de cada trade. A próxima camada será a gestão da operação, em RR (múltiplos do risco inicial). Isso mantém uma leitura objetiva entre entrada, parada e potencial de retorno.


        Primeira etapa: Implantação e análise de lucro fixo 

        Com o stop loss já implementado como input, a próxima etapa da gestão foi avaliar o uso de um take profit fixo em RR. Essa análise é importante porque o RR não representa uma distância fixa em pontos, nem uma proporção direta de ATR. Ele é uma medida proporcional ao risco inicial da operação. Assim, o stopLossTicks define a distância de 1R, enquanto o fixedTakeProfitRR define quantas vezes esse risco será utilizado como alvo.

        Essa ideia também aparece em outras abordagens de pullback. No artigo sobre os níveis de DiNapoli, por exemplo, a entrada ocorre após correções dentro de uma tendência, enquanto os objetivos são projetados por níveis de expansão, como COP, OP e XOP. O próprio artigo mostra que os níveis de expansão são usados para configurar regiões de take profit e que esses alvos fazem parte da estrutura de saída da estratégia.

        No EA desenvolvido neste artigo, a lógica foi mantida mais simples: em vez de usar níveis de Fibonacci ou projeções específicas, o alvo fixo foi definido diretamente em RR, usando o stop inicial como referência.

        O primeiro ponto importante é que o take profit em RR depende obrigatoriamente de um stop válido. Se o stop estiver desativado ou inválido, o EA não consegue calcular corretamente o tamanho de 1R. Por isso, o stop não entra apenas como proteção da operação, mas também como base matemática para a gestão proporcional.

        //+------------------------------------------------------------------+
        //| Initial take profit price                                        |
        //+------------------------------------------------------------------+
        double InitialTakeProfitPrice(const ENUM_ORDER_TYPE orderType,
                                      const double entryPrice,
                                      const double stopLossPrice)
          {
        //--- If fixedTakeProfitRR is zero, the EA does not use fixed take profit.
           if(fixedTakeProfitRR <= 0.0)
              return 0.0;
        
        //--- A fixed TP in RR requires a valid entry price and a valid initial SL.
           if(entryPrice <= 0.0 || stopLossPrice <= 0.0)
              return 0.0;
        
        //--- 1R is the distance between entry price and initial stop loss.
           double initialRisk = MathAbs(entryPrice - stopLossPrice);
        
           if(initialRisk <= 0.0)
              return 0.0;
        
        //--- Buy take profit:
        //--- the target is placed above the entry price by fixedTakeProfitRR times 1R.
           if(orderType == ORDER_TYPE_BUY)
              return NormalizePriceToTick(entryPrice + initialRisk * fixedTakeProfitRR);
        
        //--- Sell take profit:
        //--- the target is placed below the entry price by fixedTakeProfitRR times 1R.
           if(orderType == ORDER_TYPE_SELL)
              return NormalizePriceToTick(entryPrice - initialRisk * fixedTakeProfitRR);
        
           return 0.0;
          }

        Na abertura da posição, o EA calcula primeiro o stop loss. Em seguida, o take profit é projetado como múltiplo desse risco inicial. Dessa forma, stop e alvo passam a trabalhar juntos: quando o stop aumenta, o alvo em preço também aumenta; quando o stop diminui, o alvo também fica mais curto.

        //--- Estimate the entry price according to the order direction.
        double entryPrice = (isBuy ? g_ask : g_bid);
        ENUM_ORDER_TYPE orderType = (isBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL);
        
        //--- Calculate the initial stop loss first.
        //--- The stop defines the initial risk used as 1R.
        double stopLossPrice = InitialStopLossPrice(orderType, entryPrice);
        
        //--- Calculate the fixed take profit from the initial risk.
        //--- If fixedTakeProfitRR is zero, takeProfitPrice will also be zero.
        double takeProfitPrice = InitialTakeProfitPrice(orderType,
                                 entryPrice,
                                 stopLossPrice);

        Após a implantação do take profit fixo em RR, foi realizada uma otimização combinando os parâmetros stopLossTicks e fixedTakeProfitRR. O objetivo era verificar se a estratégia apresentaria uma região consistente de equilíbrio entre stop inicial e alvo fixo proporcional ao risco.

        Os ranges utilizados foram:

        • stopLossTicks: de 10 a 200
        • Passo do stop:10
        • fixedTakeProfitRR: de 0.5 a 4.0
        • Passo do RR:0.5

          Os resultados mostraram que a lógica de take profit fixo não apresentou uma concentração clara em uma região curta e bem definida. Pelo contrário, vários dos melhores resultados apareceram com stops mais altos ou com RRs também elevados, indicando que a estratégia parece precisar de espaço para o movimento se desenvolver após o pullback.

          Essa leitura é importante porque sugere que o comportamento do modelo não está alinhado com uma saída fixa, curta e objetiva. Quando os melhores resultados dependem de stops amplos ou de alvos longos, o take profit fixo passa a se comportar menos como uma regra eficiente de encerramento e mais como uma limitação artificial para movimentos que poderiam continuar.

          Por esse motivo, foi decidido manter a lógica de take profit fixo desabilitada nesta etapa. Assim, o take profit passa a funcionar apenas como uma camada testada e documentada, mas não adotada como regra principal da estratégia. A próxima análise deverá focar em formas de gestão mais progressivas, como breakeven e trailing stop, que podem proteger a operação sem limitar cedo demais a continuidade natural do movimento.


          Segunda etapa: Análise do breakeven

          A próxima camada de gestão analisada será o breakeven. O objetivo dessa regra é proteger a operação somente depois que o preço já demonstrou deslocamento favorável suficiente em relação ao risco inicial. Em vez de encerrar a posição em um alvo fixo, o breakeven procura reduzir a exposição da operação após determinado avanço em R, preservando a possibilidade de continuidade do movimento.

          Essa análise é importante porque o pullback, por natureza, depende de uma retomada após uma correção. Nem toda entrada favorável avança de forma linear; muitas vezes o preço anda a favor, retorna parcialmente e depois continua o movimento. Por isso, o breakeven precisa ser testado com cuidado. Se for ativado cedo demais, pode retirar operações que ainda estavam dentro de um comportamento normal de oscilação. Se for ativado tarde demais, pode deixar de cumprir sua função principal de reduzir risco e proteger operações que já tiveram avanço relevante.

          No código, os inputs principais do breakeven foram estruturados da seguinte forma:

          input bool   useBreakEven = true;          // Enables break even management.
          input double breakEvenActivationRR = 1.50; // Profit in R required to activate break even.
          input double breakEvenLockRR = 0.10;       // Profit in R locked after break even activation.

          A lógica utiliza o conceito de R como referência. O risco inicial da operação representa 1R, calculado pela distância entre o preço de entrada e o stop loss original. A partir disso, o EA verifica se o preço já caminhou a favor da posição o suficiente para ativar a proteção. Quando a condição é atendida, o stop é deslocado para uma região protegida, podendo ficar exatamente no ponto de entrada ou travar uma pequena fração de lucro, de acordo com o valor definido em breakEvenLockRR.

          //+------------------------------------------------------------------+
          //| Break even management                                            |
          //+------------------------------------------------------------------+
          void ManageBreakEven()
            {
          //--- If break even is disabled, no stop adjustment is applied.
             if(!useBreakEven)
                return;
          
             for(int i = PositionsTotal() - 1; i >= 0; i--)
               {
                ulong ticket = PositionGetTicket(i);
          
                if(ticket == 0 || !PositionSelectByTicket(ticket))
                   continue;
          
                if(PositionGetString(POSITION_SYMBOL) != _Symbol)
                   continue;
          
                if(!IsEAMagic((ulong)PositionGetInteger(POSITION_MAGIC)))
                   continue;
          
                ENUM_POSITION_TYPE positionType =
                   (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
          
                double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
                double currentSL  = PositionGetDouble(POSITION_SL);
                double currentTP  = PositionGetDouble(POSITION_TP);
          
                //--- Break even in RR requires an initial stop loss.
                //--- Without SL, the EA cannot calculate the initial risk.
                if(currentSL <= 0.0)
                   continue;
          
                double initialRisk = 0.0;
                double currentProfitDistance = 0.0;
                double newSL = 0.0;
          
                if(positionType == POSITION_TYPE_BUY)
                  {
                   //--- For buy positions, 1R is the distance from entry to the SL below it.
                   initialRisk = entryPrice - currentSL;
          
                   if(initialRisk <= 0.0)
                      continue;
          
                   //--- Favorable movement is measured from entry price to Bid.
                   currentProfitDistance = g_bid - entryPrice;
          
                   //--- Break even activates only after price reaches the required RR.
                   if(currentProfitDistance < initialRisk * breakEvenActivationRR)
                      continue;
          
                   //--- The new SL locks a small profit above the entry price.
                   newSL = entryPrice + initialRisk * breakEvenLockRR;
          
                   //--- Never move the stop loss backwards.
                   if(newSL <= currentSL)
                      continue;
                  }
                else
                   if(positionType == POSITION_TYPE_SELL)
                     {
                      //--- For sell positions, 1R is the distance from entry to the SL above it.
                      initialRisk = currentSL - entryPrice;
          
                      if(initialRisk <= 0.0)
                         continue;
          
                      //--- Favorable movement is measured from entry price to Ask.
                      currentProfitDistance = entryPrice - g_ask;
          
                      //--- Break even activates only after price reaches the required RR.
                      if(currentProfitDistance < initialRisk * breakEvenActivationRR)
                         continue;
          
                      //--- The new SL locks a small profit below the entry price.
                      newSL = entryPrice - initialRisk * breakEvenLockRR;
          
                      //--- Never move the stop loss backwards.
                      if(newSL >= currentSL)
                         continue;
                     }
          
                //--- Normalize the new stop loss according to the symbol tick size.
                newSL = NormalizePriceToTick(newSL);
          
                //--- Apply the new stop loss while preserving the current take profit.
                ModifyPositionStopLoss(ticket, newSL, currentTP);
               }
            }
          //+------------------------------------------------------------------+

          A função acima percorre as posições abertas do EA, identifica a direção da operação e calcula o avanço favorável em relação ao preço de entrada. Para compras, o movimento favorável é medido entre o preço de entrada e o Bid. Para vendas, a medição é feita entre o preço de entrada e o Ask. Essa diferença é importante porque respeita o preço real disponível para fechamento da posição em cada direção.

          A ativação do breakeven ocorre somente quando o deslocamento favorável atinge o múltiplo definido em breakEvenActivationRR. Por exemplo, se a ativação estiver configurada em 1.5R, o EA só moverá o stop depois que o preço andar uma vez e meia o risco inicial a favor da posição. Em seguida, o breakEvenLockRR define quanto será protegido. Se o lock estiver em 0.10R, o novo stop será colocado levemente além do preço de entrada, travando uma pequena parte do lucro.

          A modificação do stop é feita por uma função separada, responsável apenas por atualizar os níveis da posição aberta:

          //+------------------------------------------------------------------+
          //| Modify position stop loss                                        |
          //+------------------------------------------------------------------+
          bool ModifyPositionStopLoss(const ulong ticket,
                                      const double stopLossPrice,
                                      const double takeProfitPrice)
            {
             if(ticket == 0 || !PositionSelectByTicket(ticket))
                return false;
          
             MqlTradeRequest request;
             MqlTradeResult result;
          
             ZeroMemory(request);
             ZeroMemory(result);
          
          //--- This request modifies only SL and TP of an existing position.
             request.action = TRADE_ACTION_SLTP;
             request.position = ticket;
             request.symbol = _Symbol;
             request.sl = stopLossPrice;
             request.tp = takeProfitPrice;
          
             if(!OrderSend(request, result))
                return false;
          
             return (result.retcode == TRADE_RETCODE_DONE ||
                     result.retcode == TRADE_RETCODE_PLACED);
            }

          Essa separação organiza a estrutura. A função ManageBreakEven() decide quando o stop deve ser movido, enquanto ModifyPositionStopLoss() executa a alteração na posição. Isso facilita futuras expansões da gestão, pois outras regras, como trailing stop, também podem reutilizar uma função de modificação de stop. A decisão fica em ManageBreakEven(), e a execução em ModifyPositionStopLoss(). Isso evita a lógica de decisão com envio de ordem.

          Com essa estrutura, o breakeven passa a funcionar como uma camada de proteção progressiva. Ele não tenta prever o alvo final do movimento, mas apenas reduzir o risco depois que a operação já mostrou deslocamento suficiente. A análise estatística dessa etapa deve observar se essa proteção melhora o equilíbrio entre lucro, payoff, drawdown e frequência de operações, sem transformar boas entradas de pullback em saídas prematuras.

          Foi realizada uma otimização do break-even em RR no modelo OHLC, variando o breakEvenActivationRR de 1.0 até 3.0, com passo de 0.5, e o breakEvenLockRR de 0.0 até 0.3, com passo de 0.1. Pela análise dos resultados, a melhor região se concentrou em breakEvenActivationRR = 2.0, com o lock apresentando bons resultados entre 0.0 e 0.3, indicando que um BE muito precoce poderia cortar operações boas antes do desenvolvimento do movimento.

          BreakEven Optimization

          Figure 7- BreakEven Optimization

          Após a otimização inicial do breakeven em OHLC, foi realizada uma nova validação em cada tick, mantendo a ativação do BE em 2.0R e analisando o comportamento do breakEvenLockRR. Essa comparação é importante porque o breakeven, assim como o stop, é uma regra de gestão sensível ao comportamento intrabar. Mesmo com entradas baseadas em candles fechados, o deslocamento do stop ocorre em preço real, podendo apresentar diferenças entre OHLC e ticks reais.

          breakEvenLockRR Lucro OHLC Lucro cada tick Payoff OHLC Payoff cada tick DD OHLC DD cada tick
          0.0 7736 7078 8.98 8.22 9.38% 9.80%
          0.1 7631 6959 8.85 8.07 9.45% 9.87%
          0.2 7657 7004 8.86 8.11 9.45% 9.86%
          0.3 7571 6935 8.73 8.00 9.45% 9.85%

          Table 2- Break-even results in OHLC and Every Tick

          A comparação mostrou que o breakEvenLockRR = 0.0 foi o melhor valor nos dois modelos, principalmente na validação em cada tick, onde manteve o maior lucro, maior payoff e o melhor equilíbrio de drawdown. Por esse motivo, foi adotada a configuração com breakEvenActivationRR = 2.0 e breakEvenLockRR = 0.0.

          A inclusão do breakeven foi mantida porque melhorou a qualidade da gestão: houve aumento do lucro e do payoff, além de melhora no drawdown em relação à estrutura anterior. Assim, o BE passou a ser uma camada válida da estratégia, protegendo operações que já avançaram a favor sem travar lucro adicional no momento da ativação.


          Terceira etapa: Implementação do trailing stop

          Após a análise do breakeven, foi implementada a lógica de trailing stop em RR. O objetivo dessa camada é permitir que a operação continue aberta enquanto o movimento favorável se desenvolve, mas ajustando o stop de forma progressiva para proteger parte do resultado. Diferente do take profit fixo, que encerra a operação em um alvo predeterminado, o trailing acompanha o preço e só movimenta o stop quando a posição avança a favor.

          No código, os inputs principais do trailing foram estruturados da seguinte forma:

          input bool   useTrailingStop      = true; // Enables trailing stop management.
          input double trailingActivationRR = 1.50; // Profit in R required to activate trailing stop.
          input double trailingDistanceRR   = 1.00; // Distance in R from current price to trailing stop.

          A lógica calcula o risco inicial da operação como 1R e, depois, verifica se o preço já avançou o suficiente para ativar o trailing. Em compras, o novo stop é posicionado abaixo do Bid; em vendas, acima do Ask. O stop só é alterado se o novo valor for mais favorável que o stop atual.

          //+------------------------------------------------------------------+
          //| Trailing stop management                                         |
          //+------------------------------------------------------------------+
          void ManageTrailingStop()
            {
          //--- If trailing stop is disabled, no trailing adjustment is applied.
             if(!useTrailingStop)
                return;
          
          //--- Calculate the initial risk used as 1R.
             double initialRisk = InitialRiskDistance();
          
             if(initialRisk <= 0.0)
                return;
          
             for(int i = PositionsTotal() - 1; i >= 0; i--)
               {
                ulong ticket = PositionGetTicket(i);
          
                if(ticket == 0 || !PositionSelectByTicket(ticket))
                   continue;
          
                if(PositionGetString(POSITION_SYMBOL) != _Symbol)
                   continue;
          
                if(!IsEAMagic((ulong)PositionGetInteger(POSITION_MAGIC)))
                   continue;
          
                ENUM_POSITION_TYPE positionType =
                   (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
          
                double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
                double currentSL  = PositionGetDouble(POSITION_SL);
                double currentTP  = PositionGetDouble(POSITION_TP);
          
                if(entryPrice <= 0.0 || currentSL <= 0.0)
                   continue;
          
                double newSL = 0.0;
          
                if(positionType == POSITION_TYPE_BUY)
                  {
                   //--- Trailing starts only after price reaches the activation level in R.
                   if((g_bid - entryPrice) < initialRisk * trailingActivationRR)
                      continue;
          
                   //--- The new stop is placed below the current Bid.
                   newSL = g_bid - initialRisk * trailingDistanceRR;
          
                   //--- Never move the stop loss backwards.
                   if(newSL <= currentSL)
                      continue;
                  }
                else
                   if(positionType == POSITION_TYPE_SELL)
                     {
                      //--- Trailing starts only after price reaches the activation level in R.
                      if((entryPrice - g_ask) < initialRisk * trailingActivationRR)
                         continue;
          
                      //--- The new stop is placed above the current Ask.
                      newSL = g_ask + initialRisk * trailingDistanceRR;
          
                      //--- Never move the stop loss backwards.
                      if(newSL >= currentSL)
                         continue;
                     }
          
                //--- Normalize and apply the new stop while preserving the current TP.
                newSL = NormalizePriceToTick(newSL);
                ModifyPositionStopLoss(ticket, newSL, currentTP);
               }
            }
          //+------------------------------------------------------------------+

          Para avaliar essa nova camada, foi realizada uma otimização em OHLC dos parâmetros trailingActivationRR e trailingDistanceRR. A ativação foi testada de 1.0 a 3.0, com passo de 0.1, enquanto a distância do trailing foi testada de 1.0 a 2.5, também com passo de 0.1.

          Os melhores resultados ficaram concentrados em uma região relativamente clara: trailingActivationRR entre 1.4 e 2.0 e trailingDistanceRR entre 1.0 e 1.3. Essa concentração sugere que o trailing funcionou melhor quando foi ativado relativamente cedo, antes do ponto em que o breakeven começaria a atuar, mas mantendo uma distância suficiente para não interromper a operação por oscilações pequenas do preço.

          Nesta etapa, a lógica do breakeven foi desativada. Essa decisão ocorreu porque os melhores resultados em OHLC do trailing stop apresentaram ativações abaixo do nível mínimo que havia sido definido para o BE. Ou seja, o trailing começava a atuar antes do breakeven, tornando a proteção do BE redundante dentro dessa nova estrutura de gestão. Manter as duas regras ao mesmo tempo poderia gerar sobreposição e dificultar a análise isolada do trailing. Por isso, o BE foi removido temporariamente para avaliar o comportamento do trailing de forma mais limpa.

          Como o trailing stop é uma regra altamente sensível ao comportamento intrabar, essa faixa foi reavaliada em cada tick. A próxima otimização teve que verificar se essa região encontrada em OHLC permaneceu válida em uma simulação mais próxima da execução real, considerando spread, slippage e movimentações internas do candle.

          A validação em cada tick confirmou que a região encontrada no OHLC continuou coerente, mesmo com a redução natural de lucro e payoff em uma simulação mais próxima da execução real.

          trailingActivationRR trailingDistanceRR Lucro OHLC Lucro cada tick Payoff OHLC Payoff cada tick DD OHLC DD cada tick
          1.4 1.0 9766 8881 10.64 9.67 6.92% 7.77%
          2.0 1.3 9633 8833 10.82 9.92 9.02% 9.56%
          2.0 1.2 9606 8798 10.76 9.85 8.88% 9.44%
          1.9 1.3 9572 8766 10.74 9.84 9.05% 9.59%
          1.8 1.3 9567 8761 10.74 9.83 9.09% 9.64%

          Table 3- Trailing-stop results in OHLC and Every Tick

          Mesmo com a queda esperada do OHLC para Cada Tick, os melhores resultados continuaram concentrados entre 1.4 e 2.0 de ativação e entre 1.0 e 1.3 de distância. O valor escolhido foi trailingActivationRR = 1.4 e trailingDistanceRR = 1.0, pois manteve o maior lucro nos dois modelos, bom payoff e o menor drawdown tanto em OHLC quanto em cada tick entre os principais candidatos.


          Conclusão

          Neste artigo, a estratégia evoluiu de uma ideia simples de pullback para uma estrutura mais organizada, separando claramente regime, retração, gatilho, risco e gestão. O comportamento observado indica um modelo com características próximas de um swing aplicado ao day trade: um contexto mais lento define a direção, enquanto um timeframe mais rápido identifica a retração e confirma a retomada.

          Ao final, a lógica passou a reunir nove graus de liberdade. Isso ainda mantém o modelo relativamente enxuto, mas não elimina o risco de overfitting. A diferença em relação aos testes iniciais está na forma como os resultados passaram a se distribuir: em vez de depender apenas de poucos pontos isolados, surgiram regiões mais consistentes, com maior concentração de resultados positivos e melhor continuidade entre parâmetros próximos.

          Abaixo é mostrado o gráfico de resultados com variação de timeframes de regime e retração e períodos. Como se pode ver, ele teve um comportamento mais robusto comparado com os testes passados, apresentando um gradiente de pontos acima do prejuízo e mais pontos sólidos.

          Final Simulation

          Figure 8- Final Simulation

          As principais etapas introduzidas foram:

          • Expansão dos extremos da otimização para evitar escolha prematura de bordas;
          • Filtro de distância mínima em ATR para confirmar força no gatilho;
          • Stop loss por tick size, respeitando a estrutura real do ativo;
          • Comparação entre OHLC e Cada Tick nos parâmetros de gestão;
          • Teste do take profit fixo em RR;
          • Análise do breakeven;
          • Introdução do trailing stop em RR.

          O principal aprendizado foi que a otimização não deve se limitar ao melhor resultado isolado. É necessário observar regiões de estabilidade, coerência dos parâmetros, comportamento nos extremos e diferença entre modelos de simulação. Cada nova camada foi analisada para verificar se realmente melhorava a lógica ou se apenas ajustava o resultado ao histórico.


          Próximos passos

          • realizar testes fora da amostra;
          • aplicar análise walk-forward;
          • incluir custos de execução, spread e slippage mais realistas;
          • avaliar o comportamento da estratégia em diferentes períodos e regimes de mercado;
          • verificar se os parâmetros escolhidos permanecem estáveis fora da amostra utilizada na otimização.


          O EA ainda não deve ser tratado como uma versão final, mas como uma base mais sólida para as próximas etapas de validação e refinamento.


          Nome
          Tipo Descrição
          1 OneEMA_Core.mq5
          Expert Advisor

          EA de EMA Pullback com confirmação forte 

          Arquivos anexados |
          OneEMA_Core.mq5 (66.18 KB)
          Caminhe em novos trilhos: Personalize indicadores no MQL5 Caminhe em novos trilhos: Personalize indicadores no MQL5
          Vou agora listar todas as possibilidades novas e recursos do novo terminal e linguagem. Elas são várias, e algumas novidades valem a discussão em um artigo separado. Além disso, não há códigos aqui escritos com programação orientada ao objeto, é um tópico muito importante para ser simplesmente mencionado em um contexto como vantagens adicionais para os desenvolvedores. Neste artigo vamos considerar os indicadores, sua estrutura, desenho, tipos e seus detalhes de programação em comparação com o MQL4. Espero que este artigo seja útil tanto para desenvolvedores iniciantes quanto para experientes, talvez alguns deles encontrem algo novo.
          Desenvolvendo uma estratégia de pullback: Fundamentos Desenvolvendo uma estratégia de pullback: Fundamentos
          O artigo apresenta os fundamentos de uma estratégia de pullback a partir de quatro pilares: regime, retração, gatilho e gestão. A proposta é mostrar como essa lógica pode sair da leitura visual do mercado e começar a ser transformada em regras objetivas no MetaTrader 5, por meio de um EA simples e inicial. A partir desse primeiro passo, o leitor acompanha como uma ideia de trading começa a ganhar estrutura, permitindo testar hipóteses, observar resultados e preparar a evolução da estratégia de forma progressiva.
          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.
          Redes neurais em trading: sinais de negociação robustos em qualquer regime de mercado (ST-Expert) Redes neurais em trading: sinais de negociação robustos em qualquer regime de mercado (ST-Expert)
          Neste artigo, vamos conhecer o framework ST-Expert, que torna as previsões robustas diante da incerteza do mercado, permitindo levar em conta dependências locais e globais em séries temporais. Sua arquitetura flexível favorece a adaptabilidade dos modelos e aumenta a precisão das previsões.