Ajuda com Função de Take Profit

 

Boa tarde, pessoal da comunidade.

Estou desenvolvendo um EA que negocia exclusivamente o par EURUSD. A estratégia consiste em abrir uma nova posição para fazer o preço médio sempre que o preço se mover contra a posição atual. Esta parte está funcionando perfeitamente.

No entanto, estou enfrentando dificuldades com a função que posiciona os take profits em um único nível. Para ilustrar melhor, vou descrever um cenário:

Suponha que eu tenha posições de compra abertas nas seguintes cotações:

  • 1.08500
  • 1.08000
  • 1.07500

Essas posições estão com os seguintes lotes:

  • 0.01
  • 0.01
  • 0.02

A função que estou desenvolvendo (abaixo) deve ajustar o take profit dessas posições de modo que, quando o alvo for atingido, o lucro seja X (multipler) vezes maior do que a soma das perdas das posições que estão negativas. A maneira como a função está atualmente, ela retorna o erro [invalid stops].

Infelizmente, a minha função não está alcançando esse objetivo. Por isso, venho até vocês em busca de ajuda para ajustar a lógica e garantir que esse objetivo seja atingido.

Desde já, agradeço a colaboração de todos.

Obrigado!

//---
void CalculateTakeProfit(long type, double multipler)
  {
//---
   int total                 = PositionsTotal();
//---
   double  buyTarget         = 0,
           desiredProfit     = 0,
           totalLoss         = 0,
           totalLots         = 0,
           totalSwapCost     = 0,
           weightedPriceSum  = 0;
//---
   int     buys              = CountPositions(BUY);

//---
   for(int i = total - 1; i >= 0; i--)
     {
      //---
      if(!PositionSelectByTicket(PositionGetTicket(i)))
         continue;

      //---
      if(PositionGetInteger(POSITION_MAGIC) != MagicNumber || (_Symbol != "" && PositionGetSymbol(i) != _Symbol))
         continue;

      //---
      if(type == BUY && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
        {
         weightedPriceSum += PositionGetDouble(POSITION_PRICE_OPEN) * PositionGetDouble(POSITION_VOLUME);
         totalLots        += PositionGetDouble(POSITION_VOLUME);
         totalLoss        += (Ask - PositionGetDouble(POSITION_PRICE_OPEN)) * PositionGetDouble(POSITION_VOLUME);
        }
     }

//---
   if(buys > 0 && weightedPriceSum != 0)
     {
      AveragePrice  = weightedPriceSum / totalLots;
      desiredProfit = totalLoss * multipler;
      buyTarget     = AveragePrice + (desiredProfit / totalLots);
     }

//---
   for(int i = total - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);

      if(!PositionSelectByTicket(ticket))
         continue;

      if(PositionGetInteger(POSITION_MAGIC) != MagicNumber || (_Symbol != "" && PositionGetSymbol(i) != _Symbol))
         continue;

      //--- TP
      if(type == BUY && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && buyTarget > 0 && buys > 1 && NormalizeDoubles(buyTarget) != PositionGetDouble(POSITION_TP))
        {
         if(PositionModify(ticket, NormalizeDoubles(PositionGetDouble(POSITION_SL)), NormalizeDoubles(buyTarget))) {}
        }
     }
  }
 
Jeovane Reges:

Boa tarde, pessoal da comunidade.

Estou desenvolvendo um EA que negocia exclusivamente o par EURUSD. A estratégia consiste em abrir uma nova posição para fazer o preço médio sempre que o preço se mover contra a posição atual. Esta parte está funcionando perfeitamente.

No entanto, estou enfrentando dificuldades com a função que posiciona os take profits em um único nível. Para ilustrar melhor, vou descrever um cenário:

Suponha que eu tenha posições de compra abertas nas seguintes cotações:

  • 1.08500
  • 1.08000
  • 1.07500

Essas posições estão com os seguintes lotes:

  • 0.01
  • 0.01
  • 0.02

A função que estou desenvolvendo (abaixo) deve ajustar o take profit dessas posições de modo que, quando o alvo for atingido, o lucro seja X (multipler) vezes maior do que a soma das perdas das posições que estão negativas. A maneira como a função está atualmente, ela retorna o erro [invalid stops].

Infelizmente, a minha função não está alcançando esse objetivo. Por isso, venho até vocês em busca de ajuda para ajustar a lógica e garantir que esse objetivo seja atingido.

Desde já, agradeço a colaboração de todos.

Obrigado!

Eu começaria revisando NormalizeDoubles() se realmente esta normalizando como deveria e também se esta sendo obdecido o nível de STOP_LEVEL do ativo. Também daria atenção ai a forma como ta lidando com o totalloss já que ele permite valores negativos e isso não parece ser o desejado.

 
Ricardo Rodrigues Lucca #:

Eu começaria revisando NormalizeDoubles() se realmente esta normalizando como deveria e também se esta sendo obdecido o nível de STOP_LEVEL do ativo. Também daria atenção ai a forma como ta lidando com o totalloss já que ele permite valores negativos e isso não parece ser o desejado.

Boa noite @Ricardo Rodrigues Lucca, primeiramente obrigado pela ajuda.

A função  NormalizeDoubles() funciona como desejado, fiz alguns ajustes na função CalculateTakeProfit e o problema [invalid stops] foi resolvido. Também me atentei ao problema de valores negativos.

O problema agora é porque o Take Profit está sendo colocado um pouco acima do valor desejado. Por exemplo, tenho duas posições que abrem na taxa abaixo com seus respectivos lotes.

  1. 1.08801 / 0.02
  2. 1.07596 / 0.02

O Take Profit de ambas as negociações deveria ficar em 1.08498, mas a função está colocando em 1.08655. Isso se repete para todas as demais situações onde é feito o preço médio.

Abaixo envio as partes da função que fiz modificações.

//---
void CalculateTakeProfit(long type, double multipler)
  {
// Continua o mesmo código...

//---
   for(int i = total - 1; i >= 0; i--)
     {
      // Continua o mesmo código...

      //---
      if(type == BUY && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
        {
         weightedPriceSum += PositionGetDouble(POSITION_PRICE_OPEN) * PositionGetDouble(POSITION_VOLUME);
         totalLots        += PositionGetDouble(POSITION_VOLUME);

         double profit    = PositionGetDouble(POSITION_PROFIT);
         if(profit < 0)
            totalLoss += profit;
        }
     }

//---
   if(buys > 0 && weightedPriceSum != 0)
     {
      AveragePrice         = weightedPriceSum / totalLots;
      double desiredProfit = -totalLoss * multipler;
      double desiredPips   = desiredProfit / (totalLots * 100000);
      buyTarget            = NormalizeDoubles(AveragePrice + desiredPips);
     }

//---
   for(int i = total - 1; i >= 0; i--)
     {
      // Continua o mesmo código...
     }
  }


Mais uma vez obrigado pela ajuda.

 
Jeovane Reges #Boa noite @Ricardo Rodrigues Lucca, primeiramente obrigado pela ajuda. A função  NormalizeDoubles() funciona como desejado, fiz alguns ajustes na função CalculateTakeProfit e o problema [invalid stops] foi resolvido. Também me atentei ao problema de valores negativos. O problema agora é porque o Take Profit está sendo colocado um pouco acima do valor desejado. Por exemplo, tenho duas posições que abrem na taxa abaixo com seus respectivos lotes. 1.08801 / 0.02 1.07596 / 0.02 O Take Profit de ambas as negociações deveria ficar em 1.08498, mas a função está colocando em 1.08655. Isso se repete para todas as demais situações onde é feito o preço médio. Abaixo envio as partes da função que fiz modificações. Mais uma vez obrigado pela ajuda.

Boa noite, Jeovane!! Nesse seu exemplo acima, onde você fala que o TP deveria ficar em 1.08498, qual é o preço atual (BID) pra eu fazer aqui a minha simulação?

 
Vinicius Pereira De Oliveira #:

Boa noite, Jeovane!! Nesse seu exemplo acima, onde você fala que o TP deveria ficar em 1.08498, qual é o preço atual (BID) pra eu fazer aqui a minha simulação?

Boa noite, a corretora que estou usando tem spread zero para o EURUSD. Ao abrir a segunda negociação já altero o TP de ambas as negociações que estão aberta e a cada 1min verifico a taxa atual e atualizo o TP caso o preço continue andando contra.
 
Jeovane Reges #Boa noite, a corretora que estou usando tem spread zero para o EURUSD. Ao abrir a segunda negociação já altero o TP de ambas as negociações que estão aberta e a cada 1min verifico a taxa atual e atualizo o TP caso o preço continue andando contra.

Veja, pela simulação (manual) que fiz aqui, a função a seguir, sugerida pelo ChatGPT (com alguns ajustes), calcularia o TP de 1.09404 (aproximadamente) para o seu exemplo, mas, pelo que entendi, esse seria o TP correto (não 1.08498, como você sugeriu)...


Teste realizado com os dados do seu exemplo

void OnStart()
  {
   double curr_loss = 0.0, profit1 = 0.0, profit2 = 0.0;
   OrderCalcProfit(ORDER_TYPE_BUY, "EURUSD", 0.02, 1.08801, 1.07596, curr_loss);
   OrderCalcProfit(ORDER_TYPE_BUY, "EURUSD", 0.02, 1.08801, 1.09404, profit1);
   OrderCalcProfit(ORDER_TYPE_BUY, "EURUSD", 0.02, 1.07596, 1.09404, profit2);
   Print(curr_loss, "   -   ", profit1 + profit2);
  }


Função sugerida pelo ChatGPT (com ajustes)

double CalculateAverageTP(int type, double multiplier)
  {
   double total_volume = 0;
   double total_loss = 0;
   double tp_price = 0;

   double tick_size = 0.0;

   if(!SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE, tick_size))
     {
      Print(__FUNCTION__, " - Error getting the SYMBOL_TRADE_TICK_SIZE: ", GetLastError(), " - ", _Symbol);
      return 0.0;
     }

   for(int i = PositionsTotal() - 1; i >= 0; i --)
     {
      if(PositionGetTicket(i) != 0)
        {
         if(PositionGetInteger(POSITION_TYPE) == type)
           {
            double price_open = PositionGetDouble(POSITION_PRICE_OPEN);
            double volume = PositionGetDouble(POSITION_VOLUME);
            double profit = PositionGetDouble(POSITION_PROFIT);

            total_volume += volume;

            if(profit < 0.0)
              {
               total_loss += MathAbs(profit);
              }

            tp_price += price_open * volume;
           }
        }
     }

   if(total_volume == 0)
     {
      return 0.0; // No positions to calculate
     }

   tp_price /= total_volume;
   double adjusted_tp = tp_price + (multiplier * total_loss / total_volume * tick_size);

   return adjusted_tp;
  }


... Faça os testes/ajustes necessário aí no seu sistema... Se não funcionar, a culpa é do ChatGPT... Agora, se der tudo certo... O mérito é dele também. 😊👍 

 
Vinicius Pereira De Oliveira #:

Veja, pela simulação (manual) que fiz aqui, a função a seguir, sugerida pelo ChatGPT (com alguns ajustes), calcularia o TP de 1.09404 (aproximadamente) para o seu exemplo, mas, pelo que entendi, esse seria o TP correto (não 1.08498, como você sugeriu)...


Teste realizado com os dados do seu exemplo


Função sugerida pelo ChatGPT (com ajustes)


... Faça os testes/ajustes necessário aí no seu sistema... Se não funcionar, a culpa é do ChatGPT... Agora, se der tudo certo... O mérito é dele também. 😊👍 

Bom dia,

O ChatGPT me levou ao mesmo resultado de uma forma mais elegante (até adotei rs).

Para efeitos de testes fiz uma função que fecha os trades quando o lucros das posições positivas é X vezes maior do que as negativas.

O preço onde as posições são fechadas é diferente do TP. Por isso afirmo que o TP deveria ficar em 1.08498 invés de 1.08655.

Abaixo a função que faz isso. Essa função na prática (mercado real) não é muito boa, pois se o mercado tiver em forte oscilação as posições abertas acabam sendo fechadas em taxas diferentes. Por isso a "necessidade" de colocar todos os TPs em uma só taxa.

//---
bool GetProfitPositions(long type, double multiplier)
  {
   double profit    = 0;
   double sumGains  = 0;
   double sumLosses = 0;
   int total        = PositionsTotal();

   for(int i = total - 1; i >= 0; i--)
     {
      if(!PositionSelectByTicket(PositionGetTicket(i)))
         continue;

      if(PositionGetInteger(POSITION_MAGIC) != MagicNumber || (_Symbol != "" && PositionGetSymbol(i) != _Symbol))
         continue;

      if(PositionGetInteger(POSITION_TYPE) != type)
         continue;

      profit = PositionGetDouble(POSITION_PROFIT);

      if(profit > 0)
         sumGains += profit;
      else
         sumLosses += profit * (-1);
     }

   return sumGains > (sumLosses * multiplier);
  }
 
Jeovane Reges #Bom dia, O ChatGPT me levou ao mesmo resultado de uma forma mais elegante (até adotei rs). Para efeitos de testes fiz uma função que fecha os trades quando o lucros das posições positivas é X vezes maior do que as negativas. O preço onde as posições são fechadas é diferente do TP. Por isso afirmo que o TP deveria ficar em 1.08498 invés de 1.08655. Abaixo a função que faz isso. Essa função na prática (mercado real) não é muito boa, pois se o mercado tiver em forte oscilação as posições abertas acabam sendo fechadas em taxas diferentes. Por isso a "necessidade" de colocar todos os TPs em uma só taxa.

Você continua "afirmando", mas eu discordei e reproduzi o meu entendimento através de testes disponibilizados no fórum... Por favor, se você não concorda com o que demonstrei através dos testes disponibilizados, apresente a sua "afirmação" de forma que possa ser reproduzida... Com relação ao código não ser "muito bom", "elegante", não está pronto para o mercado real, essa não é a finalidade no fórum... O objetivo aqui seria sugerir formas, alternativas para você testar e fazer melhorias no SEU código para conseguir chegar aos resultados que deseja... Teste a função que sugeri, apresente os resultados, e explique o que está errado... Boa sorte aí!! 😊

 

Apenas 2 brevíssimas (mas, considero, importantes) recomendações antes de utilizar os códigos acima em mercado real:

1. A utilização de PositionSelectByTicket(PositionGetTicket(i)) é desnecessária, pois a PositionGetTicket(i) já seleciona a posição a ser trabalhada posteriormente;

2. Todas essas funções de gerenciamento de posições [PositionGetTicket(), PositionGetString(), PositionGetDouble(), PositionGetInteger(), ...] podem retornar erros que devem ser verificados/trabalhados antes de seguir com a execução do código.

 
Vinicius Pereira De Oliveira #:

Você continua "afirmando", mas eu discordei e reproduzi o meu entendimento através de testes disponibilizados no fórum... Por favor, se você não concorda com o que demonstrei através dos testes disponibilizados, apresente a sua "afirmação" de forma que possa ser reproduzida... Com relação ao código não ser "muito bom", "elegante", não está pronto para o mercado real, essa não é a finalidade no fórum... O objetivo aqui seria sugerir formas, alternativas para você testar e fazer melhorias no SEU código para conseguir chegar aos resultados que deseja... Teste a função que sugeri, apresente os resultados, e explique o que está errado... Boa sorte aí!! 😊

Bom dia!

Você está absolutamente correto. Para sair do campo teórico, envio em anexo um EA demonstrativo que desenvolvi.

Segue a descrição das principais funções que merecem atenção:

  • CalculateTakeProfit: Responsável por definir os take profits de maneira que, quando atingidos, o lucro das posições positivas seja X (multipler) vezes maior do que a soma das posições negativas.

  • GetProfitPositions: Retorna um valor booleano indicando se as posições abertas têm lucro X (multipler) vezes maior do que as perdas das posições negativas.

  • CloseAllForce: Fecha automaticamente as posições se a função GetProfitPositions retornar TRUE. Para desativá-la, basta alterar o valor da variável ClosePositions para FALSE.

  • Martingale:  Além de fazer o médio das posições negativas, também chama a função CalculateTakeProfit para ajustar o take profit das posições abertas. Essa verificação/mudança ocorre a cada 1 minuto, conforme definido na função OnTick.

No print abaixo, pode-se observar que a função CloseAllForce fecha as posições antes do Take Profit setado pela função CalculateTakeProfit. Por isso, tenho insistido que os takes estão sendo definidos em um local "errado". A razão pela qual não quero usar a função CloseAllForce no mercado real já foi mencionada anteriormente.


A estratégia do EA é apenas para efeito demonstrativo. Sei que não é o propósito, mas abaixo é apresentado o resultado do backtesting entre 01.01.2020 e 01.01.2024. Caso alguém queira aprimorá-la, fique à vontade.



Mais uma vez obrigado pela disponibilidade em ajudar.

Arquivos anexados:
EA-01.ex5  37 kb
EA-01.mq5  31 kb
 
Jeovane Reges #:

Bom dia!

Você está absolutamente correto. Para sair do campo teórico, envio em anexo um EA demonstrativo que desenvolvi.

Segue a descrição das principais funções que merecem atenção:

  • CalculateTakeProfit: Responsável por definir os take profits de maneira que, quando atingidos, o lucro das posições positivas seja X (multipler) vezes maior do que a soma das posições negativas.

  • GetProfitPositions: Retorna um valor booleano indicando se as posições abertas têm lucro X (multipler) vezes maior do que as perdas das posições negativas.

  • CloseAllForce: Fecha automaticamente as posições se a função GetProfitPositions retornar TRUE. Para desativá-la, basta alterar o valor da variável ClosePositions para FALSE.

  • Martingale:  Além de fazer o médio das posições negativas, também chama a função CalculateTakeProfit para ajustar o take profit das posições abertas. Essa verificação/mudança ocorre a cada 1 minuto, conforme definido na função OnTick.

No print abaixo, pode-se observar que a função CloseAllForce fecha as posições antes do Take Profit setado pela função CalculateTakeProfit. Por isso, tenho insistido que os takes estão sendo definidos em um local "errado". A razão pela qual não quero usar a função CloseAllForce no mercado real já foi mencionada anteriormente.


A estratégia do EA é apenas para efeito demonstrativo. Sei que não é o propósito, mas abaixo é apresentado o resultado do backtesting entre 01.01.2020 e 01.01.2024. Caso alguém queira aprimorá-la, fique à vontade.



Mais uma vez obrigado pela disponibilidade em ajudar.

NormalizeDoubles nesse ultimo EA está claramente errado. Stop level só é usado no momento de colocar a ordem. O código supostamente modificado estava intocado. Mas não é bem o ponto.

Muda a ordem no OnTick para o CloseAll ser feito depois da martingale e da open alguma coisa. Me parece que não esta tendo chance desse código executar já que as posições foram encerradas.