Expert Advisor Multiplataforma: Stops personalizados, Breakeven e Stop Móveis

Enrico Lambino | 13 novembro, 2017

Índice

  1. Introdução
  2. Stops personalizados
  3. Modificação do Nível de Stop
  4. Breakeven
  5. Stop Móvel
  6. Takeprofit Móvel
  7. Implementação
  8. CTrails (Container)
  9. Extensão da CTrail
  10. Exemplos

Introdução

No artigo anterior, o Expert Advisor Multiplaforma: Stops, mostrou como a classe CStop pode ser usada para definir os níveis de stoploss e takeprofit para uma determinada operação em um expert advisor multiplataforma. Os referidos níveis podem ser expressos em termos de pips ou pontos. No entanto, isso nem sempre é o caso para um bom número de expert advisors do mundo real. Stoploss e takeprofit muitas vezes não precisam ser calculados com base na distância do preço de entrada. Pelo contrário, nesses casos, os níveis de stoploss e/ou takeprofit são expressos em termos do preço no gráfico, geralmente como resultado de algum outro cálculo. Este artigo irá discutir sobre como usar a classe CStop para especificar os níveis do stoploss e takeprofit em termos de preço do gráfico. O artigo também discute como esses níveis de stop podem ser modificados através do uso da biblioteca destacada neste artigo. Além disso, ele também apresenta uma nova classe, a CTrail, que se parece muito com a implementação de stops personalizados. No entanto, ao contrário dos stops personalizados, a CTrail é usada principalmente para definir a evolução de um nível de stop ao longo do tempo.

Stops personalizados

No artigo anterior, mostrou-se como o stoploss e takeprofit baseado em pontos podem ser implementados nos expert advisors multiplataforma. No entanto, nem sempre esse é o caso para todos os EAs. Existem estratégias que exigem o cálculo dinâmico dos níveis de stoploss e de takeprofit, geralmente com base em dados, como as séries temporais (OHLC), bem como saídas dos indicadores técnicos.

Os níveis de stoploss e takeprofit calculados dinamicamente podem ser configurados na CStop através de seus dois métodos, StopLossCustom e TakeProfitCustom. O seguinte trecho de código mostra os dois métodos:

bool CStopBase::StopLossCustom(void)
  {
   return m_stoploss==0;
  }

bool CStopBase::TakeProfitCustom(void)
  {
   return m_takeprofit==0;
  }

O codificador ou o programador é livre para estender esses métodos para alcançar o nível desejado de stoploss ou takeprofit para qualquer instância da CStop.

Os dois métodos mencionados acima são métodos realmente sobrecarregados na classe. A seguir encontramos os métodos alternativos, que retornam valores do tipo Booleano:

bool CStopBase::StopLossCustom(void)
  {
   return m_stoploss==0;
  }

bool CStopBase::TakeProfitCustom(void)
  {
   return m_takeprofit==0;
  }

Ambas as funções retornam true se um determinado membro da classe (m_stoploss ou m_takeprofit) tiver um valor zero. O objetivo deles será discutido em breve.

A CStop calcula um nível de stop usando a seguinte orientação. O nível de stop pode ser um nível de stoploss ou de takeprofit, representado na CStop como os membros m_stoploss e m_takeprofit. Nas etapas abaixo, suponha que estavam lidando apenas com o nível de stop (m_stoploss):

  1. Se m_stoploss for zero, use o StopLossCustom no cálculo do stoploss
  2. Se m_stoploss não for zero, use o m_stoploss no cálculo do stoploss atual da operação com relação ao preço de entrada

A mesma orientação é usada para calcular o takeprofit usando o método TakeProfitCustom e o membro da classe m_takeprofit.

O uso real desses quatro métodos podem ser encontrados em duas áreas principais. Para os níveis de stop principal, a chamada do método pode ser encontrada no próprio gerenciador de ordens (COrderManager). Para os outros stops, dentro de cada instância da ordem (COrder).

Há casos em que os principais níveis de stop são enviados à corretora, juntamente com a solicitação de negociação inicial e, portanto, o expert advisor precisaria dessas informações no momento em que ele começou a processar a solicitação de negociação, e não depois que a ordem inicial foi enviada com sucesso. Isso é verdade para os stops na ponta da corretora na Metatrader 4 e na Metatrader 5 no modo hedge. Para estes modos, as informações para os níveis de stoploss e takeprofit precisam ser incluídas na solicitação de negociação da função OrderSend (MQL4, MQL5), e esses níveis de stop só se aplicam à operação principal em questão (não global).

Dentro do método TradeOpen do gerenciador de ordens, nós podemos encontrar a chamada para os dois métodos da CStop. É exibido a seguir o código da versão MQL4:

bool COrderManager::TradeOpen(const string symbol,ENUM_ORDER_TYPE type,double price,bool in_points=true)
  {
   int trades_total = TradesTotal();
   int orders_total = OrdersTotal();
   m_symbol=m_symbol_man.Get(symbol);
   if(!CheckPointer(m_symbol))
      return false;
   if(!IsPositionAllowed(type))
      return true;
   if(m_max_orders>orders_total && (m_max_trades>trades_total || m_max_trades<=0))
     {
      ENUM_ORDER_TYPE ordertype=type;
      if(in_points)
         price=PriceCalculate(type);
      double sl=0,tp=0;
      if(CheckPointer(m_main_stop)==POINTER_DYNAMIC)
        {
         sl = m_main_stop.StopLossCustom()?m_main_stop.StopLossCustom(symbol,type,price):m_main_stop.StopLossCalculate(symbol,type,price);
         tp = m_main_stop.TakeProfitCustom()?m_main_stop.TakeProfitCustom(symbol,type,price):m_main_stop.TakeProfitCalculate(symbol,type,price);
        }
      double lotsize=LotSizeCalculate(price,type,sl);
      if(CheckPointer(m_main_stop)==POINTER_DYNAMIC)
      {
         if (!m_main_stop.Broker())
         {
            sl = 0;
            tp = 0;
         }
      }
      int ticket=(int)SendOrder(type,lotsize,price,sl,tp);
      if(ticket>0)
        {
         if(OrderSelect(ticket,SELECT_BY_TICKET))
         {
            COrder *order = m_orders.NewOrder(OrderTicket(),OrderSymbol(),OrderMagicNumber(),
(ENUM_ORDER_TYPE)::OrderType(),::OrderLots(),::OrderOpenPrice());            
            if (CheckPointer(order))
            {
               LatestOrder(GetPointer(order));
               return true;
            }   
         }         
        }
     }
   return false;
  }

É exibido a seguir o código para a versão MQL5:

bool COrderManager::TradeOpen(const string symbol,ENUM_ORDER_TYPE type,double price,bool in_points=true)
  {
   bool ret=false;
   double lotsize=0.0;
   int trades_total =TradesTotal();
   int orders_total = OrdersTotal();
   m_symbol=m_symbol_man.Get(symbol);
   if(!IsPositionAllowed(type))
      return true;
   if(m_max_orders>orders_total && (m_max_trades>trades_total || m_max_trades<=0))
     {
      if(in_points)
         price=PriceCalculate(type);
      double sl=0.0,tp=0.0;
      if(CheckPointer(m_main_stop)==POINTER_DYNAMIC)
        {
         sl = m_main_stop.StopLossCustom()?m_main_stop.StopLossCustom(symbol,type,price):m_main_stop.StopLossCalculate(symbol,type,price);
         tp = m_main_stop.TakeProfitCustom()?m_main_stop.TakeProfitCustom(symbol,type,price):m_main_stop.TakeProfitCalculate(symbol,type,price);
        }
      lotsize=LotSizeCalculate(price,type,m_main_stop==NULL?0:m_main_stop.StopLossCalculate(symbol,type,price));  
      if(CheckPointer(m_main_stop)==POINTER_DYNAMIC)
      {
         if (!m_main_stop.Broker() || !IsHedging())
         {
            sl = 0;
            tp = 0;
         }
      }    
      ret=SendOrder(type,lotsize,price,sl,tp);
      if(ret)
      {
         COrder *order = m_orders.NewOrder((int)m_trade.ResultOrder(),m_trade.RequestSymbol(),(int)m_trade.RequestMagic(),
m_trade.RequestType(),m_trade.ResultVolume(),m_trade.ResultPrice());
         if (CheckPointer(order))
         {
            LatestOrder(GetPointer(order));
            return true;
         }
      }         
     }
   return ret;
  }

Embora a TradeOpen seja uma implementação dividida, nós podemos encontrar um denominador comum em termos de como as duas versões do método calculam os principais níveis de stop. Em primeiro lugar, ela calcula o stoploss e takeprofit (representado como sl e tp, respectivamente) do nível de stop principal. Isso é feito se o stoploss e o takeprofit são necessários na solicitação de negociação inicial, uma vez que a informação também pode ser necessária no cálculo dos lotes (controle de capital).

Depois que o lotes foram calculados, é importante que a informação seja redefinida para zero em certos casos. No MQL4, as variáveis ​​sl e tp são ajustadas para zero se o stop principal não for um stop na ponta da corretora:

if(CheckPointer(m_main_stop)==POINTER_DYNAMIC)
{
   if (!m_main_stop.Broker())
   {
      sl = 0;
      tp = 0;
   }
}

Na MQL5, nós estamos tentando evitar o uso do stoploss e takeprofit no modo netting, uma vez que eles se aplicam a toda a posição, não apenas ao volume da ordem de negociação que foi executada. Assim, as variáveis ​​são redefinidas para zero se o stop não for um stop na ponta da corretora OU se a plataforma estiver no modo netting:

if(CheckPointer(m_main_stop)==POINTER_DYNAMIC)
{
   if (!m_main_stop.Broker() || !IsHedging())
   {
      sl = 0;
      tp = 0;
   }
}  

Conforme discutido no artigo anterior (consulte o Expert Advisor Multiplataformas: Stops), o EA converteria os stops na ponta da corretora para ordens stop pendentes no modo netting, portanto, não há mais necessidade de enviar o stoploss e takeprofit na solicitação de negociação para o negócio inicial neste modo.

Para as negociações com vários níveis de stoploss e takeprofit, esses níveis são processados ​​assim que a negociação principal for processada com sucesso. A negociação deve ser enviada primeiro, uma vez que esses níveis de stop não terão sentido se a ordem de negociação principal de fato não ser executada no mercado. Nós podemos encontrar a geração desses outros níveis de stop dentro da inicialização da instância da COrder, disponível através do método CreateStops da COrder (da COrderBase, porque a MQL4 e a MQL5 compartilham a implementação):

void COrderBase::CreateStops(CStops *stops)
  {
   if(!CheckPointer(stops))
      return;
   if(stops.Total()>0)
     {
      for(int i=0;i<stops.Total();i++)
        {
         CStop *stop=stops.At(i);
         if(CheckPointer(stop)==POINTER_INVALID)
            continue;
         m_order_stops.NewOrderStop(GetPointer(this),stop);
        }
     }
  }

Conforme exibido no código acima, uma instância da COrderStop é criada para cada instância da CStop encontrada pelo expert advisor. Esses níveis de stop adicionais, no entanto, só são criados depois que a ordem principal é executada no mercado com sucesso.

Modificação do Nível de Stop

A CStop possui sua própria instância separada da CTradeManager e da CExpertTradeX (uma extensão da CExpertTrade, da Biblioteca Padrão MQL5). Esta instância é independente da instância encontrada no gerenciador de ordens (COrderManager), que é usado exclusivamente para manipular as entradas das negociações principais. A modificação dos níveis de stop, portanto, não são gerenciados pela COrderManager, mas pela própria instância da CStop. No entanto, uma vez que a modificação dos níveis de stop deve ser realizada por ordem, a chamada para modificar um nível de stop deve ser originada da negociação a ser modificada, ou seja, dentro da instância COrder que representa a operação.

O monitoramento dos níveis de stop começa com o método CheckStops da COrder, cujo código é mostrado abaixo:

void COrderBase::CheckStops(void)
  {
   m_order_stops.Check(m_volume);
  }

Aqui, ele chama apenas o método Check de um dos seus membros de classe, que é uma instância da COrderStops. Como nós lembramos do artigo anterior, a COrderStops é um container de ponteiros para instâncias da COrderStop. A COrderStop, por outro lado, é uma representação da CStop em uma instância da COrder.

Agora, vamos examinar o método Check da COrderStops. O método é mostrado no seguinte trecho de código:

COrderStopsBase::Check(double &volume)
  {
   if(!Active())
      return;
   for(int i=0;i<Total();i++)
     {
      COrderStop *order_stop=(COrderStop *)At(i);
      if(CheckPointer(order_stop))
        {
         if(order_stop.IsClosed())
            continue;
         order_stop.CheckTrailing();
         order_stop.Update();
         order_stop.Check(volume);
         if(!CheckNewTicket(order_stop))
            return;
        }
     }
  }

Como nós podemos ver a partir do código, ele executa pelo menos cinco funções em uma instância específica da COrderStop

  1. Ele verifica se uma instância particular da COrderStop já está fechada (método IsClosed)
  2. Ele atualiza o nível de stop com base nas instâncias trailingstop atribuídas a ele, se houver alguma (método CheckTrailing)
  3. Ele atualiza o nível de stop (método Update)
  4. Ele verifica se o mercado atingiu um determinado nível de stop (método Check)
  5. Ele verifica se um novo ID do ticket representa a negociação

Destas tarefas, a segunda tarefa é aquela relacionada com o stop móvel do stoploss ou takeprofit. A primeira tarefa é usada apenas para ver se é necessária mais alguma ação para o nível de stop (se o nível de stop foi encerrado, o EA não precisa realizar mas verificações). O terceiro método é usado quando o nível de stop foi modificado de maneira externa ao EA (por exemplo, arrastar a linha de stop para os stops virtuais). A quarta tarefa é usada para ver se o nível de stop (atualizado ou não pelo stop móvel) foi atingido pelo mercado. A quinta tarefa é usada apenas na Metatrader 4, uma vez que somente nesta plataforma o EA experimentará uma alteração no ticket da negociação quando ocorrer um encerramento parcial. A Metatrader 5 faz uma implementação mais simples neste caso, uma vez que ela mantém um registro claro da progressão da negociação.

A instância da COrderStop representa o nível de stop de uma negociação conforme definida pela CStop. Por conseguinte, qualquer stop móvel, em última análise, resultará na alteração de uma instância deste objeto de classe. O código a seguir mostra o método CheckTrailing:

bool COrderStopBase::CheckTrailing(void)
  {
   if(!CheckPointer(m_stop) || m_order.IsClosed() || m_order.IsSuspended() || 
      (m_stoploss_closed && m_takeprofit_closed))
      return false;
   double stoploss=0,takeprofit=0;
   string symbol=m_order.Symbol();
   ENUM_ORDER_TYPE type=m_order.OrderType();
   double price=m_order.Price();
   double sl = StopLoss();
   double tp = TakeProfit();
   if(!m_stoploss_closed)
      stoploss=m_stop.CheckTrailing(symbol,type,price,sl,TRAIL_TARGET_STOPLOSS);
   if(!m_takeprofit_closed)
      takeprofit=m_stop.CheckTrailing(symbol,type,price,tp,TRAIL_TARGET_TAKEPROFIT);
   if(!IsStopLossValid(stoploss))
      stoploss=0;
   if(!IsTakeProfitValid(takeprofit))
      takeprofit=0;
   return Modify(stoploss,takeprofit);
  }
A partir deste código, nós vemos que ainda segue a orientação geral de não modificar um determinado nível de stop quando esse nível de stop já foi fechado. Se o nível de stop da ordem for elegível, então ele se desloca para chamar o método CheckTrailing da instância CStop que ele representa. Agora, vamos examinar o método CheckTrailing da CStop:
double CStopBase::CheckTrailing(const string symbol,const ENUM_ORDER_TYPE type,const double entry_price,const double price,const ENUM_TRAIL_TARGET mode)
  {
   if(!CheckPointer(m_trails))
      return 0;
   return m_trails.Check(symbol,type,entry_price,price,mode);
  }

Aqui, a CStop chama o método Check de uma de suas classes, m_trails. m_trails é simplesmente um container de ponteiros para os objetos de stop móvel. O código do método é exibido abaixo:

double CTrailsBase::Check(const string symbol,const ENUM_ORDER_TYPE type,const double entry_price,const double price,const ENUM_TRAIL_TARGET mode)
  {
   if(!Active())
      return 0;   
   double val=0.0,ret=0.0;
   for(int i=0;i<Total();i++)
     {
      CTrail *trail=At(i);
      if(!CheckPointer(trail))
         continue;
      if(!trail.Active())
         continue;
      int trail_target=trail.TrailTarget();
      if(mode!=trail_target)
         continue;
      val=trail.Check(symbol,type,entry_price,price,mode);
      if((type==ORDER_TYPE_BUY && trail_target==TRAIL_TARGET_STOPLOSS) || (type==ORDER_TYPE_SELL && trail_target==TRAIL_TARGET_TAKEPROFIT))
      {
         if(val>ret || ret==0.0)
            ret=val;
      }      
      else if((type==ORDER_TYPE_SELL && trail_target==TRAIL_TARGET_STOPLOSS) || (type==ORDER_TYPE_BUY && trail_target==TRAIL_TARGET_TAKEPROFIT))
      {
         if(val<ret || ret==0.0)
            ret=val;
      }      
     }
   return ret;
  }

Neste ponto, basta entender como o container CTrails itera em suas próprias instâncias da CTrail e retorna um valor final. Este valor final é em termos do gráfico de preço do símbolo selecionado e, portanto, é do tipo double. Este é o novo valor do stoploss ou takeprofit após sua modificação bem-sucedida. Agora, voltemos ao método CheckTrailing da COrderStop, já que é deste método onde a chamada real para a modificação do nível de stop aconteceria:

bool COrderStopBase::CheckTrailing(void)
  {
   if(!CheckPointer(m_stop) || m_order.IsClosed() || m_order.IsSuspended() || 
      (m_stoploss_closed && m_takeprofit_closed))
      return false;
   double stoploss=0,takeprofit=0;
   string symbol=m_order.Symbol();
   ENUM_ORDER_TYPE type=m_order.OrderType();
   double price=m_order.Price();
   double sl = StopLoss();
   double tp = TakeProfit();
   if(!m_stoploss_closed)
      stoploss=m_stop.CheckTrailing(symbol,type,price,sl,TRAIL_TARGET_STOPLOSS);
   if(!m_takeprofit_closed)
      takeprofit=m_stop.CheckTrailing(symbol,type,price,tp,TRAIL_TARGET_TAKEPROFIT);
   if(!IsStopLossValid(stoploss))
      stoploss=0;
   if(!IsTakeProfitValid(takeprofit))
      takeprofit=0;
   return Modify(stoploss,takeprofit);
  }

O valor de retorno deste método é do tipo booleano, que é o resultado da alteração do nível de stop através do método de modificação COrderStop (retorna true se for bem-sucedido). Mas antes de enviar a ordem de modificação, ele verifica se o stoploss e o takeprofit são válidos, usando os métodos IsStopLossValid e IsTakeProfitValid. Se o valor proposto não for válido, ele será reiniciado para zero:

bool COrderStopBase::IsStopLossValid(const double stoploss) const
  {
   return stoploss!=StopLoss();
  }

bool COrderStopBase::IsTakeProfitValid(const double takeprofit) const
  {
   return takeprofit!=TakeProfit();
  }

No código acima, tanto para o stoploss quanto para o takeprofit, o valor proposto não deve ser igual ao valor atual.

Após a avaliação do stoploss e takeprofit, o método Modify da COrderStop é chamado, que é exibido abaixo:

bool COrderStopBase::Modify(const double stoploss,const double takeprofit)
  {
   bool stoploss_modified=false,takeprofit_modified=false;
   if(stoploss>0 && takeprofit>0)
     {
      if(ModifyStops(stoploss,takeprofit))
        {
         stoploss_modified=true;
         takeprofit_modified=true;
        }
     }
   else if(stoploss>0 && takeprofit==0)
      stoploss_modified=ModifyStopLoss(stoploss);
   else if(takeprofit>0 && stoploss==0)
      takeprofit_modified=ModifyTakeProfit(takeprofit);
   return stoploss_modified || takeprofit_modified;
  }

Neste ponto, três tipos de operações são realizadas, dependendo do valor do stoploss e takeprofit. Normalmente, a modificação pode ser realizada em uma ação, como no caso de stops na ponta da corretora, mas isso nem sempre é o caso. Os stops baseados em ordens pendentes, bem como os stops virtuais, devem ser processados individualmente para cada stoploss e takeprofit. O código para modificar ambos os valores pode ser encontrado nos seguintes métodos:

bool COrderStopBase::ModifyStops(const double stoploss,const double takeprofit)
  {
   return ModifyStopLoss(stoploss) && ModifyTakeProfit(takeprofit);
  }

O método ModifyStops simplesmente chama os outros dois métodos. A implementação dividida começa neste ponto, com base em dois fatores: (1) tipo do compilador usado (MQL4 ou MQL5) e (2) o tipo de stop (baseado na ponta da corretora, pendente ou virtual). Se o stop for na ponta da corretora, isso resultaria em uma solicitação de negociação modificando a posição principal. Se o stop é baseado em ordens pendentes, o EA teria que mover o preço de entrada da ordem pendente desejada. Se o stop for virtual, o EA simplesmente precisa atualizar seus dados internos referentes ao nível de stop.

A COrderStop não tem um objeto de negociação (ou um ponteiro para ele) como um dos seus membros de classe e, portanto, não é inerentemente capaz de alterar os seus próprios níveis de stop. Ele ainda precisa depender da instância CStop que ele representa para a negociação, a fim de modificar os seus próprios níveis de stop. Portanto, qualquer modificação de um nível de stop acabará resultando em uma chamada a um determinado método da CStop.

Breakeven

O break-even ou simplesmente um breakeven, é um ponto em que a receita ganha é igual ao custo. Na negociação, a receita é o lucro atual da negociação, enquanto que o custo é o spread e/ou a comissão. Este é o ponto em que uma empresa deixa o mercado com lucro/perda zero. Para os market makers, ou corretoras que não cobram comissões, o breakeven é geralmente o preço de entrada da negociação.

Em EA's, é dito que uma operação foi quebrada quando o EA alterou com êxito o o nível do stoploss para o seu preço de entrada. Ao chegar a este estado, o risco máximo da negociação se torna zero. No entanto, a posição ainda pode deixar o mercado por outros meios, como sinais de saída, geralmente com lucro.

Em uma negociação com break-even, o EA é limitado por pelo menos duas restrições:

  1. Dinâmica do stoploss
  2. A distância mínima da corretora

O comércio de uma posição deve estar sempre abaixo do preço atual do mercado para posições compradas e acima do preço do mercado para posições vendidas. Dada esta restrição, um EA pode alterar o stoploss para o breakeven somente se a posição estiver no estado de lucro não realizado.

O requisito de distância mínima da corretor ainda se aplica quando se altera o stoploss da posição, não apenas para a entrada inicial da operação. Assim, muitas vezes não é possível definir o breakeven logo a abertura de posição no mercado. O mercado tem que mover uma distância significativa a favor da posição antes que o EA possa mover o stoploss na direção do breakeven.

Dadas essas restrições, ao projetar o recurso de breakeven, nós precisamos considerar pelo menos dois fatores:

  1. O preço de ativação
  2. O novo stoploss após a ativação

O preço de ativação, ou o preço de acionamento, é o preço que o mercado tem que alcançar, para que um EA mova o stoploss de uma operação para o breakeven. Este preço de ativação deve ser avaliado de acordo com a operação, uma vez que se espera que os negócios tenham preços de entrada variáveis.

Após a ativação, o expert advisor será obrigado a alterar o stoploss para o breakeven. Normalmente, este é o preço de entrada da operação em questão, mas isso nem sempre é o caso. Para os corretores que cobram comissão, o breakeven está em algum lugar acima do preço de entrada para posições compradas, e em algum lugar abaixo do preço de entrada para posições vendidas.

Esses valores geralmente são calculados em referência a outro valor, como o preço de mercado atual e o preço de entrada da operação.

O seguinte recurso mostra um diagrama de cálculo do breakeven da maneira descrita acima. Com base neste fluxograma, os três valores de ativação, desativação e novo nível de stop são calculados de antemão. Se o nível de preço atual for maior ou igual ao nível de preço mínimo exigido para o estágio inicial (ajustando o passo para o breakeven), o novo nível de stop calculado anteriormente será usado como o novo nível de stop para a operação. Caso contrário, a saída seria zero. O próximo passo seria verificar se o novo nível de stop está dentro do nível do stop atual, que sempre deve retornar true se a condição anterior for satisfeita e, assim, retornaria o nível de stop calculado como a saída final.


Breakeven


Stop Móvel

O Stop Móvel (Trailingstop) pode ser considerado um caso especial de breakeven. Embora o breakeven seja normalmente aplicado apenas uma vez, o Stop Móvel pode ser aplicado várias vezes. Após a ativação, o stop móvel geralmente se aplica durante todo o tempo de vida da negociação. Pelo menos três fatores precisam ser considerados na concepção de um stop móvel:

  1. O preço de ativação
  2. O novo stoploss após a ativação
  3. A frequência do trailing

O recurso do stop móvel compartilha as duas primeiras variáveis ​​com o breakeven. No entanto, ao contrário do breakeven, é possível que o preço de ativação no stop móvel seja um ponto no gráfico de preços em que a negociação está em estado de perda não realizada ou "sem dinheiro". É possível que o stoploss de uma posição seja modificada enquanto a posição se encontra em prejuízo, desse modo, é possível também realizar o trailing do stoploss. Quando isso acontece, o stoploss atual da operação ainda resultará em perdas quando atingido pelo mercado, já que ele ainda não satisfaz os pré-requisitos para a ausência de perdas ou até mesmo de ganho.

O terceiro fator é a frequência do trailing do stoploss, ou o "passo". Isso determina a distância (em pontos ou pips) do nível atual do stoploss para o próximo, após o trailing inicial do stoploss. Isso torna bastante semelhante à primeira variável, que é o preço de ativação. No entanto, ao contrário do preço de ativação, o preço calculado decorrente dessa terceira variável muda com cada etapa de trailing.

Há um quarto fator em alguns stop móveis, que é o preço de desativação. Ao chegar a este ponto na negociação, o EA interrompe o trailing do stoploss. Isso também é calculado por cada operação. 

A ilustração a seguir exibe o fluxograma para o trailing do stoploss de uma posição. Isso não é muito diferente do diagrama anterior (breakeven). Se o preço for maior do que o estágio inicial do trailing, é altamente provável que o nível de stop já esteja além do estágio inicial. Caso contrário, ele ainda usaria o estágio inicial de trailing como o valor do novo nível de stop para a execução atual. Se o nível de stop ainda não excedeu o preço de ativação, então ele simplesmente segue o resto do procedimento para calcular o breakeven (o preço de desativação não se aplica ao breakeven).


Stop Móvel


Takeprofit Móvel

Trailing, trailingstop ou stop móvel geralmente se referem ao trailing do stoploss, mas é possível realizar isso com o takeprofit de uma posição também. Neste caso, o takeprofit é ajustado à medida que o preço se aproxima do nível de takeprofit (o oposto de trailing do stoploss). Teoricamente, um takeprofit que está sendo arrastado nunca será atingido. Mas isso é possível em certas situações, como os movimentos de preço de pico e gap - qualquer condição em que o mercado se mova mais rápido do que o EA este recurso pode ser favorável.

Para que o objetivo do trailing do takeprofit seja significativo, algumas coisas precisam ser satisfeitas:

  1. O preço de ativação deverá se encontrar dentro dos limites do nível de takeprofit atual
  2. O próximo preço para acionar o próximo estágio do trailing deve estar dentro dos limites do nível de takeprofit atual
  3. Idealmente, o trailing deve ser menos frequente

Se o preço de ativação estiver além do nível de takeprofit atual, o mercado atingirá o nível de takeprofit antes do preço de ativação. E assim que o takeprofit for atingido, a posição será encerrada a mercado muito antes de qualquer trailing do nível de takeprofit ser acionado. Para posições compradas, o preço de ativação deve ser inferior ao nível de takeprofit. Para posições vendidas, o preço de ativação deve ser superior ao nível de takeprofit. O mesmo é verdadeiro para a segunda condição, sendo que ela se aplica apenas as etapas adicionais após a ativação inicial.

Idealmente, o trailing deve ser menos freqüente. Isso significa que a distância entre um nível de takeprofit para o próximo (o passo) deve ser suficientemente amplo. Quanto mais frequente é o trailing do takeprofit, menor é a chance do mercado atingir o nível de takeprofit, pois o nível de takeprofit é afastado do preço atual a cada etapa de trailing.

Implementação

A CTrailBase, que serve como a classe base para a CTrail, é exibida no código abaixo:

class CTrailBase : public CObject
  {
protected:
   bool              m_active;
   ENUM_TRAIL_TARGET m_target;
   double            m_trail;
   double            m_start;
   double            m_end;
   double            m_step;
   CSymbolManager   *m_symbol_man;
   CSymbolInfo      *m_symbol;
   CEventAggregator *m_event_man;
   CObject          *m_container;
public:
                     CTrailBase(void);
                    ~CTrailBase(void);
   virtual int       Type(void) const {return CLASS_TYPE_TRAIL;}
   //--- Inicialização                    
   virtual bool      Init(CSymbolManager*,CEventAggregator*);
   virtual CObject *GetContainer(void);
   virtual void      SetContainer(CObject*);
   virtual bool      Validate(void) const;
   //--- getters e setters    
   bool              Active(void) const;
   void              Active(const bool);
   double            End(void) const;
   void              End(const double);
   void              Set(const double,const double,const double,const double);
   double            Start(void) const;
   void              Start(const double);
   double            Step(void) const;
   void              Step(const double);
   double            Trail(void) const;
   void              Trail(const double);
   int               TrailTarget(void) const;
   void              TrailTarget(const ENUM_TRAIL_TARGET);
   //--- verificação
   virtual double    Check(const string,const ENUM_ORDER_TYPE,const double,const double,const ENUM_TRAIL_TARGET);
protected:
   //--- cálculo do preço
   virtual double    ActivationPrice(const ENUM_ORDER_TYPE,const double);
   virtual double    DeactivationPrice(const ENUM_ORDER_TYPE,const double);
   virtual double    Price(const ENUM_ORDER_TYPE);
   virtual bool      Refresh(const string);
  };

O método Set permite definir as configurações de uma instância da CTrails. Ela funciona como o construtor de classe usual. Se necessário, é possível declarar um construtor personalizado que chama esse método:

void CTrailBase::Set(const double trail,const double st,const double step=1,const double end=0)
  {
   m_trail=trail;
   m_start=st;
   m_end=end;
   m_step=step;
  }

A CTrail depende dos dados de mercado para os seus cálculos. Assim, ela tem uma instância de gestão dos símbolos (CSymbolManager) como um dos seus membros de classe. É necessário atualizar o símbolo antes de qualquer outro cálculo ser realizado.

bool CTrailBase::Refresh(const string symbol)
  {
   if(!CheckPointer(m_symbol) || StringCompare(m_symbol.Name(),symbol)!=0)
      m_symbol=m_symbol_man.Get(symbol);
   return CheckPointer(m_symbol);
  }

O preço de ativação é o preço que desencadeia o movimento inicial do stoploss ou takeprofit pela instância da CTrail. Uma vez que os parâmetros de início, passo e fim são em termos de pontos, a classe precisa calcular o preço de ativação em termos do preço do gráfico. O mesmo método é usado no cálculo dos outros preços:

double CTrailBase::ActivationPrice(const ENUM_ORDER_TYPE type,const double entry_price)
  {
   if(type==ORDER_TYPE_BUY)
      return entry_price+m_start*m_symbol.Point();
   else if(type==ORDER_TYPE_SELL)
      return entry_price-m_start*m_symbol.Point();
   return 0;
  }

O cálculo do preço de desativação também segue a mesma rotina, mas desta vez usando o membro da classe m_end.

double CTrailBase::DeactivationPrice(const ENUM_ORDER_TYPE type,const double entry_price)
  {
   if(type==ORDER_TYPE_BUY)
      return m_end==0?0:entry_price+m_end*m_symbol.Point();
   else if(type==ORDER_TYPE_SELL)
      return m_end==0?0:entry_price-m_end*m_symbol.Point();
   return 0;
  }

Usar um valor de zero como preço de desativação significa que o recurso está desabilitado. O trailing será aplicado até a posição ser encerrada.

O método do preço calcula o novo valor do stoploss ou do takeprofit se as condições permitirem o deslocamento ou o trailing do stoploss no momento da avaliação:

double CTrailBase::Price(const ENUM_ORDER_TYPE type)
  {
   if(type==ORDER_TYPE_BUY)
     {
      if(m_target==TRAIL_TARGET_STOPLOSS)
         return m_symbol.Bid()-m_trail*m_symbol.Point();
      else if(m_target==TRAIL_TARGET_TAKEPROFIT)
         return m_symbol.Ask()+m_trail*m_symbol.Point();
     }
   else if(type==ORDER_TYPE_SELL)
     {
      if(m_target==TRAIL_TARGET_STOPLOSS)
         return m_symbol.Ask()+m_trail*m_symbol.Point();
      else if(m_target==TRAIL_TARGET_TAKEPROFIT)
         return m_symbol.Bid()-m_trail*m_symbol.Point();
     }
   return 0;
  }

Agora, vamos discutir o método Check. Uma instância particular da CTrail pode deslocar o stoploss ou o takeprofit. Portanto, nós precisamos apenas do recurso da CTrail para especificar se pretende alterar o stoploss ou o takeprofit da posição. Isso é alcançado pela enumeração, ENUM_TRAIL_TARGET. A declaração desta enumeração pode ser encontrada em MQLx\Common\Enum\ENUM_TRAIL_TARGET.mqh Seu código é mostrado abaixo:

enum ENUM_TRAIL_TARGET
  {
   TRAIL_TARGET_STOPLOSS,
   TRAIL_TARGET_TAKEPROFIT
  };

O método Check da classe é mostrado no seguinte código. Ao contrário dos outros métodos discutidos até agora nesta classe, esse método é um método público. Este é o método a ser chamado quando o nível do stop móvel precisar ser verificado para qualquer atualização.

double CTrailBase::Check(const string symbol,const ENUM_ORDER_TYPE type,const double entry_price,const double price,const ENUM_TRAIL_TARGET mode)
  {
   if(!Active())
      return 0;
   if(!Refresh(symbol))
      return 0;
   if(m_start==0 || m_trail==0)
      return 0;
   double next_stop=0.0,activation=0.0,deactivation=0.0,new_price=0.0,point=m_symbol.Point();
   activation=ActivationPrice(type,entry_price);
   deactivation=DeactivationPrice(type,entry_price);
   new_price=Price(type);
   if(type==ORDER_TYPE_BUY)
     {
      if (m_target==TRAIL_TARGET_STOPLOSS)
      {
         if(m_step>0 && (activation==0.0 || price>=activation-m_trail*point) && (new_price>price+m_step*point))
            next_stop=new_price;
         else next_stop=activation-m_trail*point;
         if(next_stop<price)
            next_stop=price;
         if((deactivation==0) || (deactivation>0 && next_stop>=deactivation && next_stop>0.0))
            if(next_stop<=new_price)
               return next_stop;
      }
      else if (m_target==TRAIL_TARGET_TAKEPROFIT)
      {
         if(m_step>0 && ( activation==0.0 || price>=activation) && (new_price>price+m_step*point))
            next_stop=new_price;
         else next_stop=activation+m_trail*point;
         if(next_stop<price)
            next_stop=price;
         if((deactivation==0) || (deactivation>0 && next_stop<=deactivation && next_stop>0.0))
            if(next_stop>=new_price)
               return next_stop;
      }
     }
   if(type==ORDER_TYPE_SELL)
     {
      if (m_target==TRAIL_TARGET_STOPLOSS)
      {
         if(m_step>0 && (activation==0.0 || price<=activation+m_trail*point) && (new_price<price-m_step*point))
            next_stop=new_price;
         else next_stop=activation+m_trail*point;
         if(next_stop>price)
            next_stop=price;     
         if((deactivation==0) || (deactivation>0 && next_stop<=deactivation && next_stop>0.0))
            if(next_stop>=new_price)
               return next_stop;
      }
      else if (m_target==TRAIL_TARGET_TAKEPROFIT)
      {
         if(m_step>0 && (activation==0.0 || price<=activation) && (new_price<price-m_step*point))
            next_stop=new_price;
         else next_stop=activation-m_trail*point;
         if(next_stop>price)
            next_stop=price;     
         if((deactivation==0) || (deactivation>0 && next_stop<=deactivation && next_stop>0.0))
            if(next_stop<=new_price)
               return next_stop;
      }
     }
   return 0;
  }

A partir daqui, nós podemos ver que o cálculo é diferente entre o trailing do stoploss e do takeprofit. Para o valor de Stoploss, nós gostaríamos que ele se aproximasse do mercado com cada etapa de trailing. Para o valor do takeprofit, nós queremos o contrário - empurrá-lo para longe do preço atual do mercado a uma certa distância (em pontos ou pips) com cada etapa de trailing.

CTrails (Container)

A CTrail também teria um container chamado CTrails. Sua definição é mostrada no seguinte código:

class CTrailsBase : public CArrayObj
  {
protected:
   bool              m_active;
   CEventAggregator *m_event_man;
   CStop            *m_stop;
public:
                     CTrailsBase(void);
                    ~CTrailsBase(void);
   virtual int       Type(void) const {return CLASS_TYPE_TRAILS;}
   //--- Inicialização
   virtual bool      Init(CSymbolManager*,CEventAggregator*);
   virtual CStop    *GetContainer(void);
   virtual void      SetContainer(CStop*stop);
   virtual bool      Validate(void) const;
   //--- getters e setters
   bool              Active(void) const;
   void              Active(const bool activate);
   //--- verificação
   virtual double    Check(const string,const ENUM_ORDER_TYPE,const double,const double,const ENUM_TRAIL_TARGET);
  };

O container teria que interagir entre a CStop e os objetos da CTrail que ela referencia. Ela também teria o seu próprio método Check:

double CTrailsBase::Check(const string symbol,const ENUM_ORDER_TYPE type,const double entry_price,const double price,const ENUM_TRAIL_TARGET mode)
  {
   if(!Active())
      return 0;   
   double val=0.0,ret=0.0;
   for(int i=0;i<Total();i++)
     {
      CTrail *trail=At(i);
      if(!CheckPointer(trail))
         continue;
      if(!trail.Active())
         continue;
      int trail_target=trail.TrailTarget();
      if(mode!=trail_target)
         continue;
      val=trail.Check(symbol,type,entry_price,price,mode);
      if((type==ORDER_TYPE_BUY && trail_target==TRAIL_TARGET_STOPLOSS) || (type==ORDER_TYPE_SELL && trail_target==TRAIL_TARGET_TAKEPROFIT))
      {
         if(val>ret || ret==0.0)
            ret=val;
      }      
      else if((type==ORDER_TYPE_SELL && trail_target==TRAIL_TARGET_STOPLOSS) || (type==ORDER_TYPE_BUY && trail_target==TRAIL_TARGET_TAKEPROFIT))
      {
         if(val<ret || ret==0.0)
            ret=val;
      }      
     }
   return ret;
  }

A CTrails, semelhante a outros containers usados ​​nesta série de artigos, é um descendente da CArrayObj, que é projetada para armazenar múltiplos ponteiros de instâncias da CObject e seus herdeiros. A CTrails, então, pode armazenar mais de uma instância da CTrail. No caso em que vários ponteiros para instâncias da CTrail estão presentes na CTrails, quando o método Check é chamado, ele chamaria o método Check de todas as instâncias da CTrail. No entanto, apenas o valor mais próximo do preço atual do mercado será retornado como o resultado final. Esse comportamento pode ser alterado pela extensão da CTrails.

As classes CTrail e CTrails foram reduzidas ao puro cálculo. Isso significa que todos os métodos são codificados na classe base (CTrailBase) e não em nenhuma implementação específica da linguagem. Quando chamado para verificar o status do stop móvel (pela CStop), ele obtém o novo valor do stop, e a CStop modifica o nível de stop da posição correspondente.

Extensão da CTrail

A classe CTrail não se aplica somente ao stop móvel e ao breakeven. Ela pode ser usada para definir quase qualquer evolução de um nível de stop de uma posição ao longo do tempo. O processo é muito semelhante ao modo como os stops personalizados são implementados conforme discutido neste artigo. No entanto, a mudança é aplicada estendendo os métodos da CTrail para aplicar as mudanças no nível de stop após a operação ter entrado no mercado.

Exemplos

Exemplo 1: Stops personalizados

O nosso primeiro exemplo para este artigo usa o stoploss e takeprofit personalizados para as operações que o EA entrará. Nós iremos estender o terceiro exemplo do EA do artigo anterior (ver Expert Advisor Multiplataforma: Stops) para a criação do EA. Os níveis de stop personalizados serão calculados com base nas máximas e mínimas da vela anterior. Como uma proteção adicional, adicionaremos uma condição de um nível mínimo de stop: se o cálculo do stoploss ou o takeprofit estiverem próximos do preço de entrada em menos de 200 pontos, definiremos o stoploss ou o takeprofit para 200 pontos do preço de entrada. Isso pode ajudar a garantir que nossa solicitação de neogicação seja aceita pela corretora.

Primeiro, nós declararemos um descendente para a CStop. Neste exemplo, nós chamaremos esse descendente de CCustomStop. Sua definição é mostrada abaixo:

class CCustomStop : public CStop
  {
   public:
                     CCustomStop(const string);
                    ~CCustomStop(void);
   virtual double    StopLossCustom(const string,const ENUM_ORDER_TYPE,const double);
   virtual double    TakeProfitCustom(const string,const ENUM_ORDER_TYPE,const double);
  };

Aqui, nós vamos ampliar os métodos StopLossCustom e TakeProfitCustom. O código a seguir mostra o método StopLossCustom:

double CCustomStop::StopLossCustom(const string symbol,const ENUM_ORDER_TYPE type,const double price)
  {
   double array[1];
   double val=0;
   if(type==ORDER_TYPE_BUY)
     {
      if(CopyHigh(symbol,PERIOD_CURRENT,1,1,array))
        {
         val=array[0];
        }
     }
   else if(type==ORDER_TYPE_SELL)
     {
      if(CopyLow(symbol,PERIOD_CURRENT,1,1,array))
        {
         val=array[0];
        }
     }
   if(val>0)
     {
      double distance=MathAbs(price-val)/m_symbol.Point();
      if(distance<200)
        {
         if(type==ORDER_TYPE_BUY)
            val = price+200*m_symbol.Point();
         else if(type==ORDER_TYPE_SELL)
            val=price-200*m_symbol.Point();
        }
     }
   return val;
  }

Primeiro, nós usamos as funções CopyLow e CopyHigh para calcular o stoploss dependendo do tipo de negociação (comprar ou vender). Depois de obter o valor inicial, nós obtemos o valor absoluto de sua distância do preço de entrada em pontos. Se a distância for menor do que o mínima, nós definimos a distância para a mínima.

O método TakeProfitCustom também assumi um processo semelhante, conforme mostrado abaixo:

double CCustomStop::TakeProfitCustom(const string symbol,const ENUM_ORDER_TYPE type,const double price)
  {
   double array[1];
   double val=0;
   m_symbol=m_symbol_man.Get(symbol);
   if(!CheckPointer(m_symbol))
      return 0;
   if(type==ORDER_TYPE_BUY)
     {
      if(CopyLow(symbol,PERIOD_CURRENT,1,1,array))
        {
         val=array[0];
        }
     }
   else if(type==ORDER_TYPE_SELL)
     {
      if(CopyHigh(symbol,PERIOD_CURRENT,1,1,array))
        {
         val=array[0];
        }
     }
   if(val>0)
     {
      double distance=MathAbs(price-val)/m_symbol.Point();
      if(distance<200)
        {
         if(type==ORDER_TYPE_BUY)
            val = price-200*m_symbol.Point();
         else if(type==ORDER_TYPE_SELL)
            val=price+200*m_symbol.Point();
        }
     }
   return val;
  }

As classes membro da CStop responsáveis ​​pelos valores atribuídos ao stoploos e takeprofit em pontos (m_stoploss e m_takeprofit, respectivamente) são inicializados em zero, por padrão. Assim, nós precisamos apenas colocar os comentários nas seguintes linhas dentro da função OnInit:

//main.StopLoss(stop_loss);
//main.TakeProfit(take_profit);

Se os valores (em pontos) do stoploss e takeprofit de um nível de stop estiverem em zero, somente essa instância da CStop usará o cálculo personalizado.

Exemplo 2: Breakeven

Para o recurso do breakeven, nós também modificaremos o arquivo de cabeçalho principal do terceiro exemplo do artigo anterior (o mesmo que o exemplo 1). Mas desta vez, nós não vamos estender aos objetos de classe. Em vez disso, nós apenas vamos declarar uma instância da CTrail, bem como uma instância da CTrails:

CTrails *trails = new CTrails();
CTrail *trail = new CTrail();
trail.Set(breakeven_value,breakeven_start,0);  
trails.Add(trail);
main.Add(trails);

A última linha é essencial. Nós precisamos adicionar uma instância da CTrails a uma instância existente da CStop. Desta forma, o comportamento seria aplicado a essa instância particular da CStop.

O terceiro argumento do método Set da CTrail é o passo. Seu valor padrão é 1 (1 ponto). Uma vez que nós estamos usando apenas o recurso de breakeven, nós definimos a ele um valor igual a 0.

O código acima requer as variáveis ​​breakeven_start, que é o preço de ativação (em pontos) do preço de entrada e o breakeven_value, que é a nova distância do stoploss (em pontos) do preço de ativação. Nós declararemos os parâmetros de entrada para estes dois:

input int breakeven_value = 200;
input int breakeven_start = 200;

Com esta configuração, assim que o mercado se mover pelo menos 200 pontos a favor da posição, o stoploss seria movido para 200 pontos do preço de ativação. 200 - 200 = 0, e, portanto, o novo stoploss calculado seria o preço de entrada da própria negociação.

Exemplo 3: Stop Móvel

Agora, vamos implementar o recurso de Stop Móvel em um EA. Este exemplo é muito semelhante ao exemplo anterior. Lembre-se que, do exemplo anterior, nós inserimos o seguinte código dentro da função OnInit:

CTrails *trails = new CTrails();
CTrail *trail = new CTrail();
trail.Set(breakeven_value,breakeven_start,0);  
trails.Add(trail);
main.Add(trails);

O processo não é diferente ao usar o Stop Móvel:

CTrails *trails = new CTrails();
CTrail *trail = new CTrail();
trail.Set(trail_value,trail_start,trail_step);
trails.Add(trail);   
main.Add(trails);

Desta vez, o método Set da CTrail usa três parâmetros. Os dois primeiros são idênticos ao exemplo anterior. O terceiro parâmetro é o passo, que é a frequência de trailing após a ativação. No exemplo anterior, este parâmetro do método tem um valor padrão igual 0, o que significa que nenhum trailing adicional será feito após a ativação inicial. Em seguida, nós declaramos os parâmetros de entrada trail_value, trail_start e trail_step no EA.

Exemplo 4: Trailing personalizado

Neste exemplo, deixe-nos implementar um recurso de trailing personalizado. Nós vamos estender o EA usado no primeiro exemplo deste artigo. Lembre-se de que, no expert advisor do primeiro exemplo, nós declaramos um stop personalizado que define o stoploss e o takeprofit com base nos valores da máxima e mínima da vela anterior. Neste exemplo, nós vamos expandi-lo, mas não apenas definir o stoploss e takeprofit com a entrada. Nós também queremos que o stoploss da operação siga o preço do mercado usando o preço das máximas e mínimas da vela anterior. Como medida de segurança, nós também implementaremos um stoplos para cada etapa de trailing.

Para criar este expert advisor, nós primeiro estenderemos a CTrail. Nós vamos nomear o descendente da classe como CCustomTrail. Sua definição é mostrada abaixo:

class CCustomTrail : public CTrail
  {
public:
                     CCustomTrail(void);
                    ~CCustomTrail(void);
   virtual double    Check(const string,const ENUM_ORDER_TYPE,const double,const double,const ENUM_TRAIL_TARGET);
  };

Agora, vamos ampliar o método Check. Observe que a não verificação do trailing objetivo não há problemas neste caso. Nós estamos deslocando o stoploss, e este é o alvo objetivo padrão do trailing para a CTrail:

double CCustomTrail::Check(const string symbol,const ENUM_ORDER_TYPE type,const double entry_price,const double price,const ENUM_TRAIL_TARGET mode)
  {
   if(!Active())
      return 0;
   if(!Refresh(symbol))
      return 0;
   double array[1];
   double val=0;
   if(type==ORDER_TYPE_BUY)
     {
      if(CopyLow(symbol,PERIOD_CURRENT,1,1,array))
        {
         val=array[0];
        }
     }
   else if(type==ORDER_TYPE_SELL)
     {
      if(CopyHigh(symbol,PERIOD_CURRENT,1,1,array))
        {
         val=array[0];
        }
     }
   if(val>0)
     {
      double distance=MathAbs(price-val)/m_symbol.Point();
      if(distance<200)
        {
         if(type==ORDER_TYPE_BUY)
            val = m_symbol.Ask()-200*m_symbol.Point();
         else if(type==ORDER_TYPE_SELL)
            val=m_symbol.Bid()+200*m_symbol.Point();
        }
     }
   if((type==ORDER_TYPE_BUY && val<=price+10*m_symbol.Point()) || (type==ORDER_TYPE_SELL && val>=price-10*m_symbol.Point()))
      val = 0;
   return val;
  }
Como nós podemos ver, o cálculo é bastante semelhante ao que vemos nos métodos da CCustomStop. Além disso, nas últimas partes do código, nós adicionamos a verificação do valor de retorno para que o novo stoploss proposto seja de 10 pontos (1 pip) além do anterior. Isto é para evitar que o stoploss da operação se desloque com base no valor da máxima ou mínima recente. Ao invés de subir ou descer com base no valor do preço de máxima/mínima, nós configuramos ele para que o novo nível de stopsloss seja sempre superior ao valor que o substitui (maior para posições compradas, menor para posições vendidas).

Conclusão

Neste artigo, nós demonstramos como os níveis de stop personalizados podem ser alcançados em um expert advisor multiplataforma. Em vez de definir os níveis de stoploss e takeprofit em termos de pips e pontos, os métodos apresentados introduzem uma maneira em que os referidos níveis podem ser representados em termos de valores de preço do gráfico. O artigo também demonstrou uma maneira em que os níveis de stoploss e takeprofit podem ser alterados ao longo do tempo.

Programas Utilizados ​​no Artigo

#
Nome
Tipo
Descrição
1.
breakeven_ha_ma.mqh
Arquivo de Cabeçalho
O arquivo de cabeçalho principal usado no primeiro exemplo
2.
breakeven_ha_ma.mq4 Expert Advisor
Arquivo fonte principal usado na versão em MQL4 no primeiro exemplo
3. breakeven_ha_ma.mq5 Expert Advisor Arquivo fonte principal usado na versão em MQL5 no primeiro exemplo
4. trail_ha_ma.mqh Arquivo de Cabeçalho
O arquivo de cabeçalho principal usado no segundo exemplo
5. trail_ha_ma.mq4 Expert Advisor
Arquivo fonte principal usado na versão em MQL4 no segundo exemplo
6. trail_ha_ma.mq5 Expert Advisor
Arquivo fonte principal usado na versão em MQL5 no segundo exemplo
7. custom_stop_ha_ma.mqh Arquivo de Cabeçalho O arquivo de cabeçalho principal usado no terceiro exemplo
8. custom_stop_ha_ma.mq4 Expert Advisor Arquivo fonte principal usado na versão em MQL4 no terceiro exemplo
9. custom_stop_ha_ma.mq5 Expert Advisor Arquivo fonte principal usado na versão em MQL5 no terceiro exemplo
10. custom_trail_ha_ma.mqh
Arquivo de Cabeçalho O arquivo de cabeçalho principal usado no quarto exemplo
11. custom_trail_ha_ma.mq4 Expert Advisor Arquivo fonte principal usado na versão em MQL4 no quarto exemplo
12.
custom_trail_ha_ma.mq5 Expert Advisor Arquivo fonte principal usado na versão em MQL5 no quarto exemplo

Arquivos das Classes Apresentados no Artigo

 # Nome
Tipo
 Descrição
1.
MQLx\Base\Stop\StopBase.mqh Arquivo de Cabeçalho CStop (classe base)
2.
MQLx\MQL4\Stop\Stop.mqh Arquivo de Cabeçalho CStop (versão MQL4)
3.
MQLx\MQL5\Stop\Stop.mqh Arquivo de Cabeçalho CStop (versão MQL5)
4. MQLx\Base\Trail\TrailBase.mqh  Arquivo de Cabeçalho CTrail (classe base)
5. MQLx\MQL4\Trail\Trail.mqh  Arquivo de Cabeçalho CTrail (versão MQL4)
6. MQLx\MQL5\Trail\Trail.mqh  Arquivo de Cabeçalho CTrail (versão MQL5)
7. MQLx\Base\Trail\TrailsBase.mqh  Arquivo de Cabeçalho CTrails (classe base)
8. MQLx\MQL4\Trail\Trails.mqh  Arquivo de Cabeçalho CTrails (versão MQL4)
9. MQLx\MQL5\Trail\Trails.mqh  Arquivo de Cabeçalho CTrails (versão MQL5)