Expert Advisor Universal: Um Trailing Stop Customizado (Parte 6)

Vasiliy Sokolov | 26 julho, 2016

Tabela de Conteúdos


Introdução

Este material continua a série de artigos sobre o 'Expert Advisor Universal'- um conjunto específico de classes que constituem um motor de negociação permitindo aos usuários criar suas próprias estratégias. Nos artigos anteriores, discutimos vários módulos do motor, que permitem expandir a funcionalidade básica de qualquer estratégia criada baseada nesta engenharia. No entanto, a maioria destes módulos são parte da classe CStrategy, ou objetos implementados para que esta classe atue diretamente. 

Nesta parte nós continuamos a desenvolver a funcionalidade do motor de negociação CStrategy e consideramos uma nova oportunidade: suporte a um trailing stop. Ao contrário de outros módulos do CStrategy, os algoritmos do "Trailing Stop" são externos em relação ao motor de negociação. Isto significa que a sua presença ou ausência não afeta de nenhuma maneira a funcionalidade do motor CStrategy. Esta funcionalidade pode ser implementada através de uma técnica especial de programação chamada composição. Esta técnica será descrita mais adiante neste artigo. Também continuamos a adicionar novas funções, utilizando módulos ou classes adicionais, seguindo estritamente os princípios da filosofia de programação orientada a objeto.

 

Opção de Implementação das Funções Trailing

Um "Trailing Stop" é um tipo de algoritmo cuja única tarefa é mover uma ordem de "Stop Loss" a um certo nível de preços, a fim de proteger uma posição de perda excessiva. Obviamente, pode haver uma série de algoritmos para gerir o "Stop Loss" de uma posição. Um algoritmo de controle do "Trailing Stop" pode ser implementado como um método separado dentro da classe CStrategy. Por exemplo, ela poderia receber a posição atual como um parâmetro, então retornar o nível de negociação para mover a ordem de "Stop Loss" atual para:

class CStrategy
   {
public:
   double TrailingMode1(CPosition* pos);
   };

O gerenciamento das posições de acordo com a função de trailing seria possível em linha reta dentro da estratégia:

void CMovingAverage::SupportBuy(const MarketEvent &event,CPosition *pos)
  {
   double new_sl = TrailingMode1(pos);
   pos.StopLossValue(new_sl);
  }

No entanto, uma vez que pode existir muitas funções de gerenciamento do "Trailing Stop", é altamente indesejável colocá-la dentro da classe CStrategy. Um "Trailing Stop" é um parâmetro externo em relação ao algoritmo básico do Expert Advisor. Não é uma função necessária para a operação do sistema de negociação, a sua função é apenas para simplificar o processo de negociação. Portanto a ausência da funções trailing não deve afetar o funcionamento do CStrategy ou de uma estratégia que não utiliza qualquer "Trailing Stop". Por outro lado, a existência de algoritmos de gerenciamento do stop não deve dificultar a legibilidade do código ou aumentar o código das classes base. É por isso que todas as funções "Trailing Stop" devem ser colocadas em classes e arquivos que sejam conectadas a uma estratégia de negociação com demanda separada.

Ou poderíamos implementar uma função trailing na classe CPosition. Neste caso, a operação de um "Trailing Stop" seria algo parecido com isto:

void CMovingAverage::SupportBuy(const MarketEvent &event,CPosition *pos)
  {
   pos.TrailingMode1();
  }

Porém isto apenas iria transferir o problema da classe CStrategy para a CPosition. Neste caso, a classe CPosition soaria um tanto estranha, pois apesar dos algoritmos de gerenciamento de posição serem úteis, alguns números podem ser bem grandes.  

Além disso, os algoritmos de trailing requerem a configuração de certos parâmetros. Por exemplo, para usar um "Trailing Stop" clássico precisamos especificar a distância em pips entre os valores extremos de preços alcançados e o nível de "Stop Loss que queremos gerenciar. Portanto, além dos próprios algoritmos, precisamos guardar os seus parâmetros de operação em algum lugar. Se você armazenar esses dados em classes de infra-estrutura, tais como CPosition ou CStrategy, iriam confundir as variáveis internas dessas classes com muitas variáveis, e portanto, iria complicar significativamente operação dessas classes.

 

Padronização das Funções Trailing. A classe CTrailing

Muitas vezes, comprovadamente a solução mais eficaz é a mais simples. O caso de um "Trailing Stop" não é exceção. Se imaginarmos que um "Trailing Stop" é uma classe especial que armazena os parâmetros de operação e o algoritmo para mover o stop como um método, então todos os problemas acima referente a localização dessas funções seriam resolvidos. Bem, ao criar o "Trailing Stop" como uma classe independente, os seus dados e métodos não seriam confundidos com os dados e métodos da classe base CStrategy e outros objetos importantes de infra-estrutura, como o CPosition.

Ao desenvolver esta classe, precisamos resolver duas equações.

  1. O interior da classe "Trailing Stop". Padronização de sua operação.
  2. Interação da classe com outros módulos do motor CStrategy.

Vamos considerar a primeira dessas questões. Obviamente, qualquer "Trailing Stop" tem seu conjunto único de parâmetros. Assim, qualquer padronização deste conjunto não é possível. Por outro lado, todos os algoritmos de trailing tem um parâmetro obrigatório; a posição, onde você deseja modificar a ordem de "Stop Loss". A posição será representada pela classe CPosition, que nos é familiar. Além disso, cada tipo de "Trailing Stop" deve ter um método, onde a chamada da ordem de "Stop Loss" de uma posição será modificada. Este método é um tipo de "botão", que lança o algoritmo do trailing, assim o nome do presente método deve ser o mesmo para todos os tipos de trailing.

Nós identificamos duas entidades gerais para todos os tipos de trailing, que podem ser convenientemente apresentadas sob a forma de uma classe base especial, chamada de CTrailing. Ele irá conter métodos para definir a posição atual e o método virtual "Modify", através do qual o "Stop Loss será alterado, assim como um método virtual especial "Copy", cuja finalidade será explicada mais adiante:

//+------------------------------------------------------------------+
//|                                                     Trailing.mqh |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include <Object.mqh>
#include "..\PositionMT5.mqh"
#include "..\Logs.mqh"
class CPosition;
//+------------------------------------------------------------------+
//| A classe base de um "Trailing Stop"                              |
//+------------------------------------------------------------------+
class CTrailing : public CObject
  {
protected:
   CPosition         *m_position;     // A posição que o "Trailing Stop" pretende modificar.
   CLog              *Log;
public:
                      CTrailing(void);
   void               SetPosition(CPosition *position);
   CPosition         *GetPosition(void);
   virtual bool       Modify(void);
   virtual CTrailing* Copy(void);
  };
//+------------------------------------------------------------------+
//| Construtor. Recebe um módulo registrador                         |
//+------------------------------------------------------------------+
CTrailing::CTrailing(void)
  {
   Log=CLog::GetLog();
  }
//+------------------------------------------------------------------+
//| O método de modificação do "Trailing Stop", que deve ser         |
//| substituído na classe trailing derivada                          |
//+------------------------------------------------------------------+
bool CTrailing::Modify(void)
  {
   return false;
  }
//+------------------------------------------------------------------+
//| Retorna uma cópia da instância                                   |
//+------------------------------------------------------------------+  
CTrailing* CTrailing::Copy(void)
{
   return new CTrailing();
}
//+------------------------------------------------------------------+
//| Define uma posição, o "Stop Loss" que deve ser modificado        |
//+------------------------------------------------------------------+
void CTrailing::SetPosition(CPosition *position)
  {
   m_position=position;
  }
//+------------------------------------------------------------------+
//| Retorna uma posição, o Stop Loss deve ser modificado             |
//+------------------------------------------------------------------+
CPosition *CTrailing::GetPosition(void)
  {
   return m_position;
  }
//+------------------------------------------------------------------+


Qualquer "Trailing Stop" será derivado dessa classe. A classe base do trailing não contém parâmetros de algoritmos específicos de movimento do Stop Loss. Isto ajuda a conseguir a máxima flexibilidade de operação da classe. Apesar do fato de que as funções trailing requerem vários parâmetros para a operação, o método "Modify" não aceita qualquer um deles. Todos os parâmetros são definidos diretamente nas classes filhos de trailing, usando métodos especiais. Assim, no tempo da chamada Modify, todos os parâmetros necessários já são conhecidos.

 

Interação do CTrailing com Outros Módulos da Estratégia

Nós agora temos uma classe base CTrailing, mas é o suficiente, de modo a incluí-la na estrutura geral do motor de negociação. Nós vamos adicionar a classe base dentro da composição da classe CPosition:

class CPosition
  {
public:
   CTrailing* Trailing;    // Módulo do "Trailing Stop"
  };

Este arranjo parece intuitivo e natural. Neste caso, a posição será controlada da mesma forma, por meio da própria posição:

void CMovingAverage::SupportBuy(const MarketEvent &event,CPosition *pos)
  {
   pos.Trailing.Modify();
  }

Isto também é possível através método Modify normalizado, isto é, também é conhecido com exatidão o que deve ser feito de modo a modificar a posição. 

No entanto, a integração do módulo de "Trailing Stop" não termina aí. O exemplo acima ainda requer o gerenciamento das posições ao nível da estratégia do usuário. Precisamos substituir os métodos BuySupport e SellSupport e gerenciar cada posição dentro da lógica do Expert Advisor. A fim de simplificar ainda mais o gerenciamento da posição, podemos adicionar um módulo do "Trailing Stop" diretamente para a classe CStrategy:

class CStrategy
  {
public:
   CTrailing* Trailing;   // Um módulo de "Trailing Stop" para todas as posições
  };

Precisamos também de um método CallSupport adicional que pertence à classe CStrategy:

//+------------------------------------------------------------------+
//| Chama a lógica de gerenciamento de posição, desde que o estado   |
//| da negociação não seja igual ao TRADE_WAIT.                      |
//+------------------------------------------------------------------+
void CStrategy::CallSupport(const MarketEvent &event)
  {
   m_trade_state=m_state.GetTradeState();
   if(m_trade_state == TRADE_WAIT)return;
   SpyEnvironment();
   for(int i=ActivePositions.Total()-1; i>=0; i--)
     {
      CPosition *pos=ActivePositions.At(i);
      if(pos.ExpertMagic()!=m_expert_magic)continue;
      if(pos.Symbol()!=ExpertSymbol())continue;
      if(CheckPointer(Trailing)!=POINTER_INVALID)
        {
         if(CheckPointer(Trailing)==POINTER_INVALID)
            pos.Trailing=Trailing.Copy();
         pos.Trailing.Modify();
         if(!pos.IsActive())
            continue;
        }
      if(pos.Direction()==POSITION_TYPE_BUY)
         SupportBuy(event,pos);
      else
         SupportSell(event,pos);
      if(m_trade_state==TRADE_STOP && pos.IsActive())
         ExitByStopRegim(pos);
     }
  }

Novos recursos são destacadas em amarelo. Eles são muito simples: se um "Trailing Stop" é definido como padrão, mas a posição atual não tem nenhum, então é colocado nesta posição atual, então a sua ordem de stop-loss é modificada de acordo com a lógica de trailing. No entanto, devemos levar em conta uma característica muito importante. Um exemplo de um "Trailing Stop" padrão é atribuído a cada posição, não o próprio "Trailing Stop" padrão. Esta solução ajuda a evita confusão dentro da lógica do "Trailing Stop". Imagine que a mesma instância do "Trailing Stop" gerencia várias posições. Se ele calcula qualquer variável de uma posição dentro de sua lógica e as armazena, estas variáveis deixam de ser válidas durante a próxima chamada, porque a posição transmitida pelo gerenciamento será diferente. Este fato poderia causar erros muito estranhos. A fim de evitar este problema, um exemplo individual de um "Trailing Stop" é atribuído a cada posição. Neste caso não será alterado durante toda a vida útil da posição. A fim de atribuir um "Trailing Stop" individual, é necessário copiar o "Trailing Stop" padrão. Desde que o motor CStrategy não sabe quais dados e variáveis internos precisam ser copiados, o procedimento de cópia é realizado pela classe trailing final. Ele deve substituir o método virtual Copy() da classe base CTrailing, então retorna uma referência que aponta para a cópia criada por si só sob a forma de uma classe CTrailing genérica. Aqui está um exemplo de implementação de um método copiado para o "Trailing Stop" clássico CTrailingClassic:

//+------------------------------------------------------------------+
//| Retorna uma cópia da instância                                   |
//+------------------------------------------------------------------+  
CTrailing *CTrailingClassic::Copy(void)
  {
   CTrailingClassic *tral=new CTrailingClassic();
   tral.SetDiffExtremum(m_diff_extremum);
   tral.SetStepModify(m_step_modify);
   tral.SetPosition(m_position);
   return tral;
  }

O método cria uma instância do tipo CTrailingClassic, define os seus parâmetros iguais aos parâmetros do exemplo atual, em seguida devolve um objeto na forma de ponteiro ao tipo CTrailing.

Lembre-se de uma regra simples ao desenvolver uma classe trailing personalizada: 

A fim de definir um "Trailing Stop" padrão, é necessário substituir o método Copy da classe base CTrailing. Caso contrário, o CStrategy seria incapaz de gerenciar automaticamente as posições abertas. Se você pretende usar apenas o "Trailing Stop" nos métodos BuySupport e SellSupport, não é necessário substituir o método virtual Copy.

A exigência de substituir o método Copy complica o desenvolvimento de um "Trailing Stop" personalizado, mas faz com que o comportamento do módulo da lógica seja mais seguro, prevenindo o aparecimento de erros no processamento de dados comum.

Agora o CStrategy pode gerenciar posições usando o "Trailing Stop" transmitido como uma lógica de gestão. Se ligar o ponteiro CStrategy::Trailing em qualquer algoritmo de "Trailing Stop" no construtor de estratégia personalizado, ele se tornará um "Trailing Stop" padrão para todas as posições que pertencem ao Expert Advisor corrente. Portanto, não é preciso substituir os métodos BuySupport e SellSupport nas estratégias que usam apenas o trailing para gerenciar posições. As posições serão geridas automaticamente no nível CStrategy.

Observe que no código CallSupport, a chamada do CTrailing::Modify é seguida por uma verificação da posição, se está ativa ou não. Isto significa que não seria problema o fechamento de uma posição durante o processo de modificação do trailing stop, poiso ciclo de pesquisa interromperia a chamada de métodos substituídos e continuaria a pesquisa com a posição seguinte. Esta característica causa resultados interessantes:

Qualquer algoritmo de gerenciamento da posição pode ser efetivamente usado como uma função de "Trailing Stop". A modificação da ordem de "Stop Loss" não é necessária. O algoritmo de gerenciamento da posição fecha a posição sob certas condições. Esta ação é esperada e o CStrategy iria processar normalmente.


Uso Prático do Trailing Stop. Um exemplo de "Trailing Stop" clássico

Agora, quando a classe base é definida e temos "ensinado" ao motor de estratégia básica interagir com ela, podemos criar uma implementação específica dos "Trailing Stops". Vamos começar com um algoritmo clássico de "Trailing Stop". Seu funcionamento é muito simples. Um "Trailing Stop" move a ordem de "Stop Loss" de uma posição seguindo as novas máximas (para uma posição comprada) e mínimas (para uma posição vendida) dos preços. Se o preço vai retorna, o "Stop Loss" permanece no mesmo nível. Assim, a "Stop Loss" está orientado a seguir o preço a uma certa distância. Esta distância é definida pelo parâmetro correspondente.

Além disso, o "Trailing Stop" tem mais um parâmetro. É opcional. Para evitar uma modificação demasiadamente frequente do "Stop Loss", vamos introduzir uma limitação adicional: a diferença mínima de um novo nível do Stop Loss do nível anterior deve ser igual aos pontos do parâmetro StepModify. O valor StepModify é definido em parâmetro separado. Este parâmetro é importante para a negociação em FORTS. De acordo com regras de negociação do FORTS, a Bolsa cobra uma taxa adicional nas chamadas "transações ineficientes". Se houver muitas modificações de Stop Loss, enquanto existem algumas negociações reais - a Bolsa vai exigir uma taxa adicional do trader. Assim, os algoritmos devem ter esta característica dentro da conta.

Aqui está o código do nosso primeiro "Trailing Stop". Baseia-se na classe CTrailing e substitui o método Modify:  

//+------------------------------------------------------------------+
//|                                              TrailingClassic.mqh |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include "Trailing.mqh"
//+------------------------------------------------------------------+
//| Integrar os parâmetros do trailing stop na lista de parâmetros   |
//| do Expert Advisor                                                |
//+------------------------------------------------------------------+
#ifdef SHOW_TRAILING_CLASSIC_PARAMS
input double PointsModify=0.00200;
input double StepModify=0.00005;
#endif
//+------------------------------------------------------------------+
//| Um "Trailing Stop" clássico                                      |
//+------------------------------------------------------------------+
class CTrailingClassic : public CTrailing
  {
private:
   double            m_diff_extremum;  // A distância em pontos a partir de um extremo para a posição do "Stop Loss
   double            m_step_modify;    // O número mínimo de pontos para modificar o Stop Loss
   double            FindExtremum(CPosition *pos);
public:
                     CTrailingClassic(void);
   void              SetDiffExtremum(double points);
   double            GetDiffExtremum(void);
   void              SetStepModify(double points_step);
   double            GetStepModify(void);
   virtual bool      Modify(void);
   virtual CTrailing *Copy(void);
  };
//+------------------------------------------------------------------+
//| Construtor. Inicializa os parâmetros padrões                     |
//+------------------------------------------------------------------+
CTrailingClassic::CTrailingClassic(void) : m_diff_extremum(0.0),
                                           m_step_modify(0.0)
  {
#ifdef SHOW_TRAILING_CLASSIC_PARAMS
   m_diff_extremum=PointsModify;
   m_step_modify=StepModify;
#endif
  }
//+------------------------------------------------------------------+
//| Retorna uma cópia da instância                                   |
//+------------------------------------------------------------------+  
CTrailing *CTrailingClassic::Copy(void)
  {
   CTrailingClassic *tral=new CTrailingClassic();
   tral.SetDiffExtremum(m_diff_extremum);
   tral.SetStepModify(m_step_modify);
   tral.SetPosition(m_position);
   return tral;
  }
//+------------------------------------------------------------------+
//| Define o número de pontos a partir de um extremo alcançado       |
//+------------------------------------------------------------------+
void CTrailingClassic::SetDiffExtremum(double points)
  {
   m_diff_extremum=points;
  }
//+------------------------------------------------------------------+
//| Define o valor de uma modificação mínima nos pontos              |
//+------------------------------------------------------------------+
void CTrailingClassic::SetStepModify(double points_step)
  {
   m_step_modify=points_step;
  }
//+------------------------------------------------------------------+
//| Retorna o número de pontos a partir de um extremo alcançado      |
//+------------------------------------------------------------------+
double CTrailingClassic::GetDiffExtremum(void)
  {
   return m_diff_extremum;
  }
//+------------------------------------------------------------------+
//| Retorna o valor de uma modificação mínima em pontos              |
//+------------------------------------------------------------------+
double CTrailingClassic::GetStepModify(void)
  {
   return m_step_modify;
  }
//+------------------------------------------------------------------+
//| "Trailing Stop" modificado de acordo com a lógica de um clássico |
//| trailing stop                                                    |
//+------------------------------------------------------------------+
bool CTrailingClassic::Modify(void)
  {

   if(CheckPointer(m_position)==POINTER_INVALID)
     {
      string text="Posição inválida para o "Trailing Stop" atual. Define a posição com o método 'GetPosition'";
      CMessage *msg=new CMessage(MESSAGE_WARNING,__FUNCTION__,text);
      Log.AddMessage(msg);
      return false;
     }
   if(m_diff_extremum<=0.0)
     {
      string text="Set points trailing-stop with 'SetDiffExtremum' method";
      CMessage *msg=new CMessage(MESSAGE_WARNING,__FUNCTION__,text);
      Log.AddMessage(msg);
      return false;
     }
   double extremum=FindExtremum(m_position);
   if(extremum == 0.0)return false;
   double n_sl = 0.0;
   if(m_position.Direction()==POSITION_TYPE_BUY)
      n_sl=extremum-m_diff_extremum;
   else
      n_sl=extremum+m_diff_extremum;
   if(n_sl!=m_position.StopLossValue())
      return m_position.StopLossValue(n_sl);
   return false;
  }
//+------------------------------------------------------------------+
//| Retorna o preço do extremo alcançado mantendo a                  |
//| posição. Para uma posição comprada, irá retornar o maior         |
//| preço alcançado. Para uma vendida - o menor preço que foi        |
//| alcançado.                                                       |
//+------------------------------------------------------------------+
double CTrailingClassic::FindExtremum(CPosition *pos)
  {
   double prices[];
   if(pos.Direction()==POSITION_TYPE_BUY)
     {
      if(CopyHigh(pos.Symbol(),PERIOD_M1,pos.TimeOpen(),TimeCurrent(),prices)>1)
         return prices[ArrayMaximum(prices)];
     }
   else
     {
      if(CopyLow(pos.Symbol(),PERIOD_M1,pos.TimeOpen(),TimeCurrent(),prices)>1)
         return prices[ArrayMinimum(prices)];
     }
   return 0.0;
  }
//+------------------------------------------------------------------+


O código básico da classe está contido nos métodos Modify e FindExtremum. O EA procura a máxima ou a mínima de um preço (dependendo do tipo de posição) a partir do histórico usando o método FindExtremum. Devido a isso, mesmo depois de reiniciar a estratégia ou após o seu tempo ocioso, o "Stop-Loss" será calculado corretamente.

Nossa classe "Trailing Stop" contém construções de programação adicionais ainda obscuros na forma do macro SHOW_TRAILING_CLASSIC_PARAMS e algumas variáveis de entrada 'input'. Vamos discutir essas construções mais adiante na seção separada: "Adicionando parâmetros de trailing para as configurações do Expert Advisor".

 

Adicionando um Trailing Stop na Estratégia CImpulse

No artigo anterior "Expert Advisor Universal: O Uso de Ordens Pendentes e Suporte de Cobertura (Hedge)" introduziu pela primeira vez a estratégia CImpulse. Sua estratégia de negociação simples é baseada em entradas durante os movimentos de preços acentuados. A estratégia proposta aplica-se a gestão de posições com base nos valores da Média Móvel. A estratégia fecha uma posição comprada quando uma barra abre abaixo da Média Móvel. A estratégia fecha uma posição vendida quando uma barra abre acima da Média Móvel. Mais uma vez, aqui está o código, conforme descrito no artigo anterior, que implementa esta lógica:

//+------------------------------------------------------------------+
//| Gerenciando uma posição comprada de acordo com a Média Móvel     |
//+------------------------------------------------------------------+
void CImpulse::SupportBuy(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))return;
   ENUM_ACCOUNT_MARGIN_MODE mode = (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
   {
      double target = Bid() - Bid()*(m_percent/100.0);
      if(target < Moving.OutValue(0))
         pos.StopLossValue(target);
      else
         pos.StopLossValue(0.0);
   }
   if(Bid() < Moving.OutValue(0))
      pos.CloseAtMarket();
}
//+------------------------------------------------------------------+
//| Gerenciando uma posição vendida de acordo com a Média Móvel      |
//+------------------------------------------------------------------+
void CImpulse::SupportSell(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))return;
   ENUM_ACCOUNT_MARGIN_MODE mode = (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
   {
      double target = Ask() + Ask()*(m_percent/100.0);
      if(target > Moving.OutValue(0))
         pos.StopLossValue(target);
      else
         pos.StopLossValue(0.0);
   }
   if(Ask() > Moving.OutValue(0))
      pos.CloseAtMarket();
}

Vamos simplificá-lo usando a lógica do "Trailing Stop" clássico em vez das regras de gerenciamento de posições. Vamos excluir os métodos a partir do código da estratégia e será adicionado um "Trailing Stop" clássico como uma função padrão do trailing no construtor da estratégia. Vamos renomear a estratégia: CImpulseTrailingAuto:

//+------------------------------------------------------------------+
//| Configuração da inicialização da estratégia e trailing stop      |
//| no início                                                        |
//+------------------------------------------------------------------+
CImpulseTrailing::CImpulseTrailing(void)
{
   CTrailingClassic* classic = new CTrailingClassic();
   classic.SetDiffExtremum(0.00100);
   Trailing = classic;
}

Agora, de acordo com a nova lógica, a saída da posição baseia-se no "Trailing Stop", que está a uma distância de 0.00100 pontos do alcance do preço extremo .

O código fonte completo da estratégia CImpulse com um "Trailing Stop" automático está disponível no arquivo ImpulseTrailingAuto.mqh.

 

Adicionando Parâmetros de Trailing nas Configurações do Expert Advisor

O método de operação do "Trailing Stop" que criamos é muito prático. Mas ainda é preciso configurar os parâmetros de "Trailing Stop" no âmbito de estratégia personalizada. Precisamos simplificar o processo: por exemplo, de alguma forma adicionar parâmetros do "Trailing Stop" para as configurações do Expert Advisor. O problema é que, se o "Trailing Stop" não é utilizado, os parâmetros ainda existem dentro das configurações do Expert Advisor e podem causar ambiguidade. Para evitar este problema, podemos usar uma compilação condicional. Vamos adicionar os parâmetros do "Trailing Stop" e as macros compilações condicionais SHOW_TRAILING_CLASSIC_PARAMS ao módulo de um "Trailing Stop" clássico:

//+------------------------------------------------------------------+
//|                                              TrailingClassic.mqh |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include "Trailing.mqh"
//+------------------------------------------------------------------+
//| Integrar os parâmetros do trailing stop na lista de parâmetros   |
//| do Expert Advisor                                                |
//+------------------------------------------------------------------+
#ifdef SHOW_TRAILING_CLASSIC_PARAMS
input double PointsModify = 0.00100;
input double StepModify =   0.00005;
#endif
//+------------------------------------------------------------------+
//| Um "Trailing Stop" clássico                                      |
//+------------------------------------------------------------------+
class CTrailingClassic : public CTrailing
  {
   ...
public:
                     CTrailingClassic(void);
   ...
  };
//+------------------------------------------------------------------+
//| Construtor. Inicializa os parâmetros padrões                     |
//+------------------------------------------------------------------+
CTrailingClassic::CTrailingClassic(void) : m_diff_extremum(0.0),
                                           m_step_modify(0.0)
  {
   #ifdef SHOW_TRAILING_CLASSIC_PARAMS
   m_diff_extremum = PointsModify;
   m_step_modify = StepModify;
   #endif
  }

Agora que o macro SHOW_TRAILING_CLASSIC_PARAMS foi definido, os parâmetros de trailing serão integrados às configurações do Expert Advisor durante a compilação:


Fig. 1. Dinamicamente ligados aos parâmetros PointsModify e StepModify.

Quando o macro SHOW_TRAILING_CLASSIC_PARAMS é comentado ou não está disponível, as configurações do "Trailing Stop" desaparecem dos parâmetros do EA:


Fig 2. Parâmetros desabilitados de um "Trailing Stop"

O macro SHOW_TRAILING_CLASSIC_PARAMS acrescenta parâmetros trailing para as configurações do EA e, adicionalmente, configura o CTrailingClassic de modo que os parâmetros de configuração são automaticamente adicionados a ele no momento da criação. Assim, quando a estratégia cria o macro, ela já contém os parâmetros introduzidos pelo utilizador através da janela de configuração do Expert Advisor. 

 

Média Móvel Baseado no Trailing Stop

Na parte anterior deste artigo, a estratégia CImpulse fecha suas posições quando o preço se move acima ou abaixo da média móvel. A média móvel foi apresentada na forma de indicador da classe CIndMovingAverage. A classe CIndMovingAverage é muito semelhante à classe "Trailing Stop". Calcula-se os valores da média móvel e permite configurações flexíveis aos parâmetros do indicador. A única diferença em relação ao "Trailing Stop" é a ausência de um algoritmo de gerenciamento de posição. A classe CIndMovingAverage não tem o método Modify(). Por outro lado, a classe base CTrailing já inclui todos os métodos necessários, mas tem algoritmos para trabalhar com a média móvel. O método de composição torna possível combinar as vantagens de cada uma destas classes e criar um novo tipo de "Trailing Stop" baseado nestas classes: um trailing stop com base na média móvel. O funcionamento do algoritmo é muito simples: ele estabelece um nível de Stop Loss de uma posição igual à média móvel. Vamos também adicionar uma verificação adicional ao método Modify: se o preço atual está abaixo (posição comprada) ou acima (posiçao vendida) do nível de stop loss calculado, a posição deve ser fechada nos preços atuais do mercado. A classe inteira está disponível abaixo:

//+------------------------------------------------------------------+
//|                                               TrailingMoving.mqh |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include "Trailing.mqh"
#include "..\Indicators\MovingAverage.mqh"

//+------------------------------------------------------------------+
//| "Trailing Stop" com base na MovingAverage.  Define um stop loss  |
//| de uma posição igual ao nível da MM                              |
//+------------------------------------------------------------------+
class CTrailingMoving : public CTrailing
{
public:
   virtual bool       Modify(void);
   CIndMovingAverage* Moving;
   virtual CTrailing* Copy(void);
};
//+------------------------------------------------------------------+
//| Define o "Stop Loss" de uma posição igual ao nível da MM         |
//+------------------------------------------------------------------+
bool CTrailingMoving::Modify(void)
{
   if(CheckPointer(Moving) == POINTER_INVALID)
      return false;
   double value = Moving.OutValue(1);
   if(m_position.Direction() == POSITION_TYPE_BUY &&
      value > m_position.CurrentPrice())
      m_position.CloseAtMarket();
   else if(m_position.Direction() == POSITION_TYPE_SELL &&
      value < m_position.CurrentPrice())
      m_position.CloseAtMarket();
   else if(m_position.StopLossValue() != value)
      return m_position.StopLossValue(value);
   return false;
}
//+------------------------------------------------------------------+
//| Retorna uma cópia exata da instância CTrailingMoving             |
//+------------------------------------------------------------------+
CTrailing* CTrailingMoving::Copy(void)
{
   CTrailingMoving* mov = new CTrailingMoving();
   mov.Moving = Moving;
   return mov;
}

A função Modify compara o nível de média móvel com o nível do stop atual. Se os níveis não são iguais, a função coloca um novo nível de stop para a posição. O valor da média móvel da barra concluída anteriormente é usado, porque barra atual está sempre no processo de formação. Note também que o indicador MovingAverage é declarado como um ponteiro. Este recurso permite que o usuário conecte qualquer objeto do tipo CIndMovingAverage a este "Trailing Stop". 

Agora vamos testar a operação da classe no Testador de Estratégia. Aqui está um pequeno vídeo mostrando o seu funcionamento:

 

 

Trailing Stop Individual Para Uma Posição

Analisamos o mecanismo de operação do "Trailing Stop". Através da utilização do método virtual unificado Modify, o motor de negociação CStrategy pode configurar automaticamente um "Trailing Stop" para cada posição e chamar o algoritmo de cálculo. Normalmente, isso é o suficiente, mas em alguns casos talvez seja necessário gerenciar cada posição individualmente. Isso significa que podemos precisar aplicar um tipo de trailing para uma posição e outro tipo diferente para outra posição. Tais características de trailing não podem ser unificadas ou implementadas em paralelo no motor de negociação, portanto o controle de tais "Trailing Stops" devem ser implementados dentro da estratégia. Isso pode ser feito pela substituição dos métodos BuySupport e SellSupport. Neste caso não há necessidade de inicializar um "Trailing Stop" padrão da forma que fizemos no construtor de estratégia personalizada.

Suponha que as posições compradas da estratégia CImpulse devem ser geridas através de um "Trailing Stop"com base na Média Móvel. Um "Trailing Stop" clássico deveria ser aplicado sobre as posições vendidas. Estes tipos de trailings foram descritos anteriormente. Vamos substituir os métodos BuySupport e SellSupport da seguinte forma:

//+------------------------------------------------------------------+
//| Gerenciando uma posição comprada de acordo com a Média Móvel     |
//+------------------------------------------------------------------+
void CImpulseTrailing::SupportBuy(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))
      return;
   if(pos.Trailing == NULL)
   {
      CTrailingMoving* trailing = new CTrailingMoving();
      trailing.Moving = GetPointer(this.Moving);
      pos.Trailing = trailing;
   }
   pos.Trailing.Modify();
}
//+------------------------------------------------------------------+
//| Gerenciando uma posição vendida de acordo com a Média Móvel      |
//+------------------------------------------------------------------+
void CImpulseTrailing::SupportSell(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))
      return;
   if(pos.Trailing == NULL)
   {
      CTrailingClassic* trailing = new CTrailingClassic();
      trailing.SetDiffExtremum(0.00100);
      pos.Trailing = trailing;
   } 
   pos.Trailing.Modify();
}

Observe que para o "Trailing Stop" baseado na média móvel, a classe CIndMovingAverage é definida como um parâmetro. Esta classe está disponível na estratégia como um objeto Moving. Numa linha de código, temos instruído o "Trailing Stop" ao objeto a ser usado no cálculo do "Stop Loss". 

No método SupportSell, um outro tipo de trailing é aplicado para novas posições, este "Trailing Stop" tem seu próprio conjunto de parâmetros. O trailing usa um canal de 0.00100 pips para que seja formado o preço extremo. 

O código completo da estratégia CImpulse com os métodos de trailing individuais para cada tipo de posição está disponível no arquivo ImpulseTrailingManual.mqh.

 

Uma Pequena Lista de Correções na Versão Mais Recente

O motor de negociação CStrategy foi muito alterado desde sua primeira publicação na primeira parte do artigo. Nós adicionamos novas funções e módulos que ampliam as possibilidades de negociação. Além disso, várias versões do compilador com várias mudanças foram lançados desde a primeira publicação. Algumas das mudanças eram incompatíveis com as versões antigas do CStrategy, então tivemos que modificar o motor de negociação. Essas correções e extensões causaram o aparecimento de erros inevitáveis. Nesta seção, vamos cobrir as correções de erros a partir da versão mais recente do motor de negociação.

  • Um painel da estratégia de negociação foi incluído no projeto. Por causa de um erro na versão anterior do compilador, o painel foi desativado nas partes 3 e 4 devido a problemas de compatibilidade. Após as correções de compilação, o painel foi adicionado novamente na parte 5, mas não funcionou adequadamente. Na sexta edição do sistema de negociação, o funcionamento do painel foi completamente restaurado.
  • O painel de negociação continha um erro: em vez de exibir "SellOnly", ele mostrava o modo "BuyOnly" duas vezes. Este bug foi corrigido.
  • Nas versões anteriores, alterando o modo de negociação no painel, não ocorria o modo de negociaçao da estratégia. O bug foi corrigido no sexta versão.
  • Um novo comportamento foi adicionado ao processo de mudança do modo: no modo SellOnly, todas as posições Compradas são fechadas e, adicionalmente, todos as ordens pendentes de Compra pertencentes à estratégia são excluídas. O mesmo se aplica para BuyOnly: todas as ordens de Venda pendentes são cancelados. Quando você seleciona o modo "Stop", todos as ordens pendentes em ambos os sentidos são excluídas.

Por favor, relatar quaisquer bugs encontrados no funcionamento do motor. Falhas detectadas serão corrigidas. 


Conclusão

Novas funções foram adicionadas ao motor de negociação na sexta parte do artigo. Agora o motor negociação suporta "Trailing Stop". Cada "Trailing Stop" é uma classe especial que contém o método Modify padronizado que modifica o nível do "Stop Loss". Ele também contém dados e parâmetros que configuram o algoritmo do movimento do trailing. O trailing pode ser utilizado de duas maneiras.

  • A operação pode ser implementada num motor de negociação ou a estratégia pode ser ativada no modo Autopilot. Neste último caso, um "Trailing Stop" padrão será aplicado automaticamente na posição. Nenhum controle a partir da estratégia personalizada é necessário neste caso. 
  • O gerenciamento do "Trailing Stop" paralelo a estratégia personalizada. Neste caso, a estratégia personalizada gerencia suas próprias posições utilizando a função "Trailing Stop" . Este modo permite implementar uma lógica de controle complexa - por exemplo, usando diferentes algoritmos de trailing para diferentes tipos de posição.
Cada "Trailing Stop" contém informações completas sobre a posição que ele gerencia. Além disso, o método de modificação de posição podem fechá-la a qualquer momento. Este método proporciona um elevado grau de flexibilidade as algoritmos de trailing. Formalmente, qualquer algoritmo de controle de posição pode atuar como algoritmo de "Trailing Stop" . Por exemplo, em vez de alterar o Stop Loss, o algoritmo pode alterar o nível de ordem do "Take Profit". Essas modificações não quebram a lógica de negociação, enquanto o motor negociação ainda funciona corretamente.