English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Como criar o seu próprio limite móvel

Como criar o seu próprio limite móvel

MetaTrader 5Indicadores | 26 dezembro 2013, 15:33
16 964 0
Dmitry Fedoseev
Dmitry Fedoseev

Introdução

Antes de começarmos com o assunto deste artigo, eu acho que é uma boa ideia colocar os pingos nos is e cruzar os ts. Mais uma vez nós definimos os termos "posição" e "pedido":

  • Posição - é um compromisso do negócio, ou seja, número de contratos comprados ou vendidos para um instrumento financeiro. Pode existir apenas uma posição para um instrumento.
  • Pedido - é uma instrução para o corretor comprar ou vender um instrumento financeiro. Existem diversos tipos de pedidos: mercado e pendentes, assim como pedidos de parada (Stop Loss e Take Profit).

Figura 1. Posições e pedidos.

Figura 1. Posições e pedidos

Este artigo foca no acompanhamento dos níveis de Stop Loss para posições. Para pedidos pendentes, esta operação não faz nenhum sentido, porque você pode seguir diretamente para o preço do pedido. E quanto ele se transforma em uma posição (ou sua parte), então este material será útil.

A posição de negócio pode ser fechada não somente pressionando o botão "Fechar" na caixa de diálogo da posição. (Figura 2)

Figura 2. Fechando uma posição usando o botão

Figura 2. Fechando uma posição usando o botão "Fechar" na caixa de diálogo da posição. 1 - abra o menu contextual da posição, 2 - selecione "Fechar posição"
3- Clique no botão "Fechar"

Adicionalmente, a posição pode ser fechada automaticamente quando o preço alcançar um nível pré-determinado de lucro (Take Profit), ou de prejuízo (Stop Loss). Ao contrário de fechar uma posição utilizando o botão "Fechar", fechar por Stop Loss e Take Profit não é feito pelo terminal (pelo negociante ou especialista, mas pelo investidor ou corretor. Assim, a posição de fechamento é totalmente garantida, independente da conexão ou fornecimento de energia. Isto torna o uso do Stop Loss um elemento praticamente obrigatório no trabalho do negociante.

A única ação que o negociador deve tomar - é dar uma ordem ao corretor para estabelecer os níveis de parada protetora. Nestas palavras, você deve estabelecer o Stop Loss (Parar Perdas) na posição (ou abrir a posição com esse nível estabelecido). A configuração de Stop Loss é feita usando o comando "Modificar" do menu contextual no terminal. Na lista de posições, selecione uma posição, clique com o botão direito e escolha "Modificar ou Deletar". Então na caixa de diálogo da posição você precisa inserir o nível necessário de Stop Loss e clicar "Modificar". (Figura 3)

Figura 3. Configurando o Nível de Stop Loss da posição.

Figura 3. Configurando o Nível de Stop Loss da posição. 1- abra o menu contextual da posição, 2- clique "Modificar ou deletar", 3 - estabeleça o valor, 4 - clique "Modificar"

Os níveis de Stop Loss da posição são mostrados na tabela de preço juntamente com o seu nível de abertura. (Figura 4)

Figura 4. Posição com Stop Loss. O nível é marcado com uma linha pontilhada vermelha rotulada com sl na sua borda esquerda.

Figura 4. Posição com Stop Loss. O nível é marcado com uma linha pontilhada vermelha rotulada com sl na sua borda esquerda

Você pode não somente instalar Stop Loss para uma posição, mas também alterar periodicamente seu valor. Por exemplo, você pode elevá-lo quando o preço muda para uma direção lucrativa, desse modo reduzindo a perda possível. Tal elevação do nível de proteção é conhecido como parada móvel.

Existem muitas variações de parada móvel: você pode simplesmente usar a Stop Loss depois que o preço estiver a uma certa distância. Você pode começar a mover o Stop Loss não imediatamente, mas quando a posição atingir certa lucratividade, então ele é movido imediatamente para o nível de ponto de equilíbrio. Esta variante é padrão e embutida no terminal do cliente MetaTrader 5. Para usar a parada móvel padrão, clique com o botão direito em posição e selecione "Trailing stop" (Parada Móvel ou Stop Móvel) (Figura 5).

Figura 5. Habilitando a parada móvel padrão no terminal.

Figura 5. Habilitando a parada móvel padrão no terminal. 1- abra o menu contextual da posição, 2- clique "Trailling Stop", 3 - selecione o valor (ou configure o valor).
O comando de configuração do valor (Personalizado)
está na base do menu contextual, e não é mostrado na imagem

Em adição ao monitoramento direto de preço, a parada móvel pode trabalhar na base de algum indicador técnico. Por exemplo, baseado nas médias móveis, isto permite não reagir a mudanças de curto prazo do preço, baseado no indicador Ichimoku ou mais apropriado; e até no indicador Parabolic SAR (Parada e Reverso), que não é planejado inicialmente para este propósito. Veja a Figura 6.

Figura 6. Indicador Parabolic SAR

Figura 6. Indicador Parabolic SAR

Na programação processual do MQL4, uma parada móvel tem sido criada usualmente como uma função separada ou tem sido integrado em outras funções. Por exemplo, na Amostra do Especialista MACD, incluído no MetaTrader 4, a função de parada móvel é integrada com a função de fechamento de pedidos de mercado.

for(cnt=0;cnt<total;cnt++)
  {
   OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES);
   if(OrderType()<=OP_SELL &&         // check for opened position 
      OrderSymbol()==Symbol())        // check for symbol
     {
      if(OrderType()==OP_BUY)         // long position is opened
        {
         // should it be closed?
         if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious && 
            MacdCurrent>(MACDCloseLevel*Point))
           {
            OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet); // close position
            return(0); // exit
           }
         // check for trailing stop
         if(TrailingStop>0) 
           {
             
            if(Bid-OrderOpenPrice()>Point*TrailingStop)
              {
               if(OrderStopLoss()<Bid-Point*TrailingStop)
                 {
                  OrderModify(OrderTicket(),OrderOpenPrice(),Bid-Point*TrailingStop,OrderTakeProfit(),0,Green);
                  return(0);
                 }
              }
           }
        }
      else // go to short position
        {
         // should it be closed?
         if(MacdCurrent<0 && MacdCurrent>SignalCurrent && 
            MacdPrevious<SignalPrevious && MathAbs(MacdCurrent)>(MACDCloseLevel*Point))
           {
            OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet); // close position
            return(0); // exit
           }
         // check for trailing stop
         if(TrailingStop>0) 
           {
             
            if((OrderOpenPrice()-Ask)>(Point*TrailingStop))
              {
               if((OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0))
                 {
                  OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop,OrderTakeProfit(),0,Red);
                  return(0);
                 }
              }
           }
        }
     }
  }

MQL5 como Linguagem orientada à objeto fornece muito mais possibilidades na criação de especialistas. Ele lhe permite criar classes versáteis e multifuncionais que podem posteriormente ser rapidamente integrada em praticamente qualquer especialista. Neste artigo nós devolveremos tal classe.


1. Criando uma classe base de parada móvel

Como mencionado acima, existem um grande número de limites móveis, mas eles todos tem aspectos funcionais em comum:

  • Determinar tipo (direção) da posição;
  • Determinar o nível atual de Stop Loss da posição;
  • Calcular o novo nível de Stop Loss;
  • Checar para necessidade de alteração no nível atual de Stop Loss;
  • Modificar o nível de Stop Loss da posição.

O tipo da parada móvel determinará apenas o valor do nível calculado de Stop Loss. Assim, a funcionalidade básica da parada móvel será incluída na classe base. Para funcionalidade, que depende do tipo de parada móvel, subclasses serão criadas. Aplicação aos métodos destas subclasses será feita através de métodos virtuais da classe base.

Uma vez que nós estamos planejando usar indicadores técnicos, para assegurar sua operação estável, é necessário prover uma aplicação prática periódica a eles. Para este propósito nós usaremos o temporizador. Nós também planejamos ligar/desligar limites móveis (quando usando classes como parte de um sistema de negócio mecânico), e depois ligar/desligar usando o botão - objeto gráfico (quando usando classes como parte de especialistas auxiliares). De acordo com estes requerimentos funcionais, a classe base terá o seguinte conjunto de métodos:

class CTrailingStop
  {
protected:
public:
   void CTrailingStop(){};
   void ~CTrailingStop(){};
   void Init(){};                   // Initialization of class
   bool StartTimer(){};             // Start timer
   void StopTimer(){};              // Stop timer
   void On(){};                     // Turn on trailing stop
   void Off(){};                    // Turn off trailing stop
   bool DoStoploss(){};             // Main method of controlling level of Stop Loss position
   void EventHandle(){};            // Method of processing chart events (pressing button to turn on trailing stop)
   void Deinit(){};                 // Deinitialization
   virtual bool Refresh(){};        // Refresh indicator
   virtual void Setparameters(){};  // Setting parameters and loading indicator
   virtual int Trend(){};           // Trend shown by indicator
   virtual double BuyStoploss(){};  // Stop Loss value for the Buy position
   virtual double SellStoploss(){}; // Stop Loss value for the Sell position
  };

Quando nomeando o método Init(), ele aceitará os parâmetros gerais que não dependem do tipo da parada móvel usada. O método configurará o modo de parada móvel e preparar as variáveis com alguns parâmetros de mercado.

  • StartTimer() - será usado para iniciar o temporizador, necessário para envio periódico para indicadores e para mantê-los forçadamente as provisões do terminal.
  • StopTimer() - será usado para parar o temporizador no encerramento de trabalho do especialista.
  • On() - habilitando a parada móvel e configurando o botão para modo pressionado (se o botão é utilizado).
  • Off() - desabilitando parada móvel e configurando o botão para modo não pressionado (se o botão é utilizado).
  • DoStoploss() - Método principal de manutenção do nível de Stop Loss da posição.
  • EventHandle() - utilizado para processamento de eventos de gráfico, particularmente para responder para pressionar o botão e ligar/desligar a parada móvel, dependendo da posição do botão.
  • Deinit() - opera quando o especialista termina o trabalho, garante a liberação do controle do indicador.
  • Refresh()- provém atualização dos valores indicadores. Este método é necessário para determinar os valores atuais dos indicadores, antes de calcular os valores de Stop Loss. Além disto, este método é utilizado independentemente - é chamado periodicamente por temporizador para manter os indicadores em condições de trabalho.
  • SetParameters() - quando você aciona este método ele aceita parâmetros indicadores, o indicador é carregado com os parâmetros específicos.
  • Tren() - método de determinação de média, demonstrada pelo indicador. Se o indicador demonstrar a direção acima, ele retorna valor 1, se a baixo - retorna -1.
  • Os métodos BuyStoploss() e SellStoploss() retornarão os novos valores de Stop Loss para situações de compra e venda, calculados pelo indicador.

1.1. Método Init()

O método Init() é o primeiro método, chamado após criar uma instância de classe. Ele aceita os parâmetros gerais, independente do tipo de parada móvel; símbolo, prazo, modo de parada móvel (por turnos ou por barras), para anexar ou não anexador indicador a planilha, criar ou não criar botão. Assim então ele aceita as propriedades do botão: Coordenada X do botão, coordenada Y do botão, cor do botão, cor do título do botão.

Os parâmetros, necessários para funcionamento futuro, são armazenados nas variáveis da classe. Além disto, quando o método Init() funciona, ele determina os parâmetros principais imutáveis de mercado, necessários para a parada móvel: o número de dígitos depois do ponto e o valor do ponto. Finalmente, dependendo do tipo da parada móvel, o nome do botão e seu título são formados. Se ele é configurado para usar um botão, ele é criado.

Na seção "protegida" vamos nomear todas as variáveis necessárias:

protected:
string m_symbol;             // symbol
ENUM_TIMEFRAMES m_timeframe; // timeframe
bool m_eachtick;             // work on each tick
bool m_indicator;            // show indicator on chart
bool m_button;               // show "turn on/turn off" button
int m_button_x;              // x coordinate of button
int m_button_y;              // y coordinate of button
color m_bgcolor;             // button color
color m_txtcolor;            // button caption color
int m_shift;                 // bar shift
bool m_onoff;                // turned on/turned off
int m_handle;                // indicator handle
datetime m_lasttime;         // time of trailing stop last execution
MqlTradeRequest m_request;   // trade request structure
MqlTradeResult m_result;     // structure of trade request result
int m_digits;                // number of digits after comma for price
double m_point;              // value of point
string m_objname;            // button name
string m_typename;           // name of trailing stop type
string m_caption;            // button caption

Agora vamos escrever o método Init() em si:

//--- Trailing stop initialization method
void Init(string             symbol,
          ENUM_TIMEFRAMES timeframe,
          bool   eachtick  =   true,
          bool   indicator =  false,
          bool   button    =  false,
          int    button_x  =      5,
          int    button_y  =     15,
          color  bgcolor   = Silver,
          color  txtcolor  =   Blue)
  {
//--- set parameters
   m_symbol    = symbol;    // symbol
   m_timeframe = timeframe; // timeframe
   m_eachtick  = eachtick;  // true - work on each tick, false - false - work once per bar 
//--- set bar, from which indicator value is used
   if(eachtick)
     {
      m_shift=0; // created bar in per tick mode
     }
   else
     {
      m_shift=1; // created bar in per bar mode
     }
   m_indicator = indicator; // true - attach indicator to chart
   m_button    = button;    // true - create button to turn on/turn off trailing stop
   m_button_x  = button_x;  // x coordinate of button
   m_button_y  = button_y;  // y coordinate of button
   m_bgcolor   = bgcolor;   // button color
   m_txtcolor  = txtcolor;  // button caption color 
//--- get unchanged market history 
   m_digits=(int)SymbolInfoInteger(m_symbol,SYMBOL_DIGITS); // number of digits after comma for price
   m_point=SymbolInfoDouble(m_symbol,SYMBOL_POINT);         // value of point 
//--- creating button name and button caption
   m_objname="CTrailingStop_"+m_typename+"_"+symbol;        // button name
   m_caption=symbol+" "+m_typename+" Trailing";             // button caption 
//--- filling the trade request structure
   m_request.symbol=m_symbol;                               // preparing trade request structure, setting symbol
   m_request.action=TRADE_ACTION_SLTP;                      // preparing trade request structure, setting type of trade action
//--- creating button
   if(m_button)
     {
      ObjectCreate(0,m_objname,OBJ_BUTTON,0,0,0);                 // creating
      ObjectSetInteger(0,m_objname,OBJPROP_XDISTANCE,m_button_x); // setting x coordinate
      ObjectSetInteger(0,m_objname,OBJPROP_YDISTANCE,m_button_y); // setting y coordinate
      ObjectSetInteger(0,m_objname,OBJPROP_BGCOLOR,m_bgcolor);    // setting background color
      ObjectSetInteger(0,m_objname,OBJPROP_COLOR,m_txtcolor);     // setting caption color
      ObjectSetInteger(0,m_objname,OBJPROP_XSIZE,120);            // setting width
      ObjectSetInteger(0,m_objname,OBJPROP_YSIZE,15);             // setting height
      ObjectSetInteger(0,m_objname,OBJPROP_FONTSIZE,7);           // setting font size
      ObjectSetString(0,m_objname,OBJPROP_TEXT,m_caption);        // setting button caption 
      ObjectSetInteger(0,m_objname,OBJPROP_STATE,false);          // setting button state, turned off by default
      ObjectSetInteger(0,m_objname,OBJPROP_SELECTABLE,false);     // user can't select and move button, only click it
      ChartRedraw();                                              // chart redraw 
     }
//--- setting state of trailing stop
   m_onoff=false;                                                 // state of trailing stop - turned on/turned off, turned off by default 
  };

Você pode ver que ao criar o nome e título do botão, a variável m_typename é utilizada, que não foi inicializada com nenhum valor. Atribuir um valor a ela será feito no construtor da subclasse. Então, quando utilizando métodos diferentes de parada móvel ele terá valor diferente, correspondendo ao tipo de parada móvel usada.

1.2. Método StartTimer()

O método StartTimer() iniciar o temporizador comum do especialista.

//--- Start timer
bool StartTimer()
  {
   return(EventSetTimer(1));
  };

Quando utilizando um temporizador, você deve adicionar o chamado do método Refresh() na função OnTimer() para atração periódica para o indicador.

1.3. Método StopTimer()

O método StopTimer() para o temporizador de um especialista.

//--- Stop timer
void StopTimer()
  {
   EventKillTimer();
  };

Quando o especialista encerra seu o trabalho, este método para o temporizador, caso você o tenha utilizado. Este método será chamado quando executando o método Deinit() de uma classe.

1.4. Método On()

O método On() liga a parada móvel. Ligar é feito pela atribuição de valor real à variável m_onoff. Se botão é configurado para ser usado na inicialização da classe, então ele é pressionado.

//--- Turn on trailing stop
void On()
  {
   m_onoff=true; 
   if(m_button)
     { // if button is used, it is "pressed"
      if(!ObjectGetInteger(0,m_objname,OBJPROP_STATE))
        {
         ObjectSetInteger(0,m_objname,OBJPROP_STATE,true);
        }
     }
  }

1.5. Método Off()

O método Off() desliga a parada móvel. Desligar é feito pela atribuição de valor falso à variável m_onoff. Se o botão é configurado para ser usado na inicialização da classe, então ele é desapertado.

//--- Turn off trailing stop
void Off()
  {
   m_onoff=false;
   if(m_button)
     { // if button is used, it is "depressed"
      if(ObjectGetInteger(0,m_objname,OBJPROP_STATE))
        {
         ObjectSetInteger(0,m_objname,OBJPROP_STATE,false);
        }
     }
  }

1.6. Método EventHandle()

O método EventHandle() será nomeado a partir da função OnChartEvent() e consequentemente serão aceitos todos os parâmetros transmitidos a função OnChartEvent().

//--- Method of tracking button state - turned on/turned off
void EventHandle(const int id,const long  &lparam,const double &dparam,const string &sparam)
  {
   if(id==CHARTEVENT_OBJECT_CLICK && sparam==m_objname)
     { // there is an event with button
      if(ObjectGetInteger(0,m_objname,OBJPROP_STATE))
        { // check button state
         On(); // turn on
        }
      else
        {
         Off(); // turn off
        }
     }
  }

Se o evento CHARTEVENT_OBJECT_CLICK ocorrer, e estes eventos ocorrem com o botão que tem o nome m_objname (nome do objeto, com o qual o evento ocorreu, é passado na variável sparam), então dependendo do estado do botão, os métodos On() ou Off() são executados.

1.7. Método Deinit()

O método Deinit deve ser chamado quando o especialista termina o seu trabalho. Este método para o temporizador, libera o controle dos indicadores e deleta o botão, caso este tenha sido usado.

//--- Method of deinitialization
void Deinit()
  {
   StopTimer();                  // stop timer
   IndicatorRelease(m_handle);   // release indicator handle
   if(m_button)
     {
      ObjectDelete(0,m_objname); // delete button
      ChartRedraw();             // chart redraw
     }
  }

Os métodos virtuais Refresh(), SetParameters(), Tren(), ButStoploss() e SellStoploss() serão discutidos posteriormente - quando nós criaremos subclasses de parada móvel. Agora vamos considerar o método principal da classe base - o método DoStoploss().

1.8. Método DoStoploss()

O método DoStoploss() é o método funcional principal, o qual deve ser nomeado a partir da função OnTick() em cada turno. Se o valor da variável m_onoff é falso (limite variável desligado), o método imediatamente termina o seu trabalho.

if(!m_onoff)
  {
   return(true);// if trailing stop is turned off
  }

Posteriormente, se a parada móvel estiver funcionando no modo por barra, então o tempo é checado - comparando o tempo da barra criada com o tempo da última execução bem sucedida de função. Se o tempo corresponde, então o método finaliza sua atividade.

datetime tm[1];
// get the time of last bar in per bar mode 
if(!m_eachtick)
  { 
   // if unable to copy time, finish method, repeat on next tick 
   if(CopyTime(m_symbol,m_timeframe,0,1,tm)==-1)
     {
      return(false); 
     }
   // if the bar time is equal to time of method's last execution - finish method
   if(tm[0]==m_lasttime)
     { 
      return(true);
     }
  }

Se a parada móvel está ligada e a checagem de tempo estiver ocorrido, então a parte principal do método é executada - valores indicadores são atualizados (o método Refresh() é chamado).

if(!Refresh())
  { // get indicator values
   return(false);
  }

Então, dependendo do valor retornado pelo método Trend(), a parada móvel, seja para posição de compra ou venda, é executada.

// depending on trend, shown by indicator, do various actions
switch (Trend())
  {
   // Up trend
   case 1: 
      // code of trailing stop for the buy position
      break;
   // Down trend
   case -1: 
      // code of trailing stop for the sell position
      break;
  }

Considere sua função no exemplo para a posição de compra.

if(PositionSelect(m_symbol,1000))
  {   //--- select position. if succeeded, then position exists
   if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
     {//--- if position is buy

      //--- get Stop Loss value for the buy position
      sl=BuyStoploss(); 
      //--- find out allowed level of Stop Loss placement for the buy position
      double minimal=SymbolInfoDouble(m_symbol,SYMBOL_BID)-m_point*SymbolInfoInteger(m_symbol,SYMBOL_TRADE_STOPS_LEVEL);
      //--- value normalizing
      sl=NormalizeDouble(sl,m_digits); 
      //--- value normalizing
      minimal=NormalizeDouble(minimal,m_digits); 
      //--- if unable to place Stop Loss on level, obtained from indicator, 
      //    this Stop Loss will be placed on closest possible level
      sl=MathMin(sl,minimal); 
      //--- value of Stop Loss position
      double possl=PositionGetDouble(POSITION_SL); 
      //--- value normalizing
      possl=NormalizeDouble(possl,m_digits); 
      if(sl>possl)
        {//--- if new value of Stop Loss if bigger than current value of Stop Loss, 
         //    an attempt to move Stop Loss on a new level will be made
         //--- filling request structure
         m_request.sl=sl; 
         //--- filling request structure
         m_request.tp=PositionGetDouble(POSITION_TP); 
         //--- request
         OrderSend(m_request,m_result); 
         if(m_result.retcode!=TRADE_RETCODE_DONE)
           {//--- check request result
            //--- log error message
            printf("Unable to move Stop Loss of position %s, error #%I64u",m_symbol,m_result.retcode); 
            //--- unable to move Stop Loss, finishing
            return(false); 
           }
        }
     }
  }

Se a posição pode ser selecionada, seu tipo é conferido. Se o tipo da posição corresponde com a média, então, usando o método BuyStoploss(), nós obtemos o valor requerido de Stop Loss (na variável sl). Depois, determine o nível permitido, no qual o Stop Loss pode ser configurado. Se o nível calculado é próximo do permitido, ajuste o valor da variável sl. Então nós obtemos o valor atual da posição de Stop Loss (na variável possl), e comparamos alores das variáveis sl e possl. Se o novo valor da identidade do Stop Loss é melhor que o valor atual - modifique a posição.

Antes que a modificação preencha os campos sl e tp da estrutura do MqlTradeRequest. A variável m_request.sl é atribuída com o valor requerido de Stop Loss, a variável m_request.tp - com o valor existente de Take Profit (o deixe imutado). O resto dos campos estão sendo preenchidos quando o método Init() é executado. Após preencher a estrutura a função OrderSend() é chamada.

Ao encerrar o seu trabalho, o valor da variável m_result.retcod é verificado. Se o valor não for igual a TRADE_RETCODE_DONE, então por alguma razão foi impossível realizar ação, requerida pela função OrderSend(). Ao mesmo tempo em uma mensagem de registro com o número de erros e realização do método é executada. Se a função OrderSend() for terminada com sucesso, então recorde a barra de tempo, na qual o último trabalho do método DoStoploss foi feito. No caso de erro, mesmo no modo por barra, no próximo turno uma tentativa de retentar o método será feita. Tentativas continuarão por tão longo quanto se complete o seu trabalho com sucesso.

Abaixo esta todo o código do método DoStopLoss().

bool DoStoploss()
  {
//--- if trailing stop is turned off
   if(!m_onoff)
     {
      return(true);
     }
   datetime tm[1];
//--- get the time of last bar in per bar mode
   if(!m_eachtick)
     {
      //--- if unable to copy time, finish method, repeat on next tick 
      if(CopyTime(m_symbol,m_timeframe,0,1,tm)==-1)
        {
         return(false);
        }
      //--- if the bar time is equal to time of method's last execution - finish method
      if(tm[0]==m_lasttime)
        {
         return(true);
        }
     }
//--- get indicator values
   if(!Refresh())
     {
      return(false);
     }
   double sl;
//--- depending on trend, shown by indicator, do various actions
   switch(Trend())
     {
      //--- Up trend
      case 1:
         //--- select position. if succeeded, then position exists
         if(PositionSelect(m_symbol))
           {
            //--- if position is buy
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
              {
               //--- get Stop Loss value for the buy position
               sl=BuyStoploss();
               //--- find out allowed level of Stop Loss placement for the buy position
               double minimal=SymbolInfoDouble(m_symbol,SYMBOL_BID)-m_point*SymbolInfoInteger(m_symbol,SYMBOL_TRADE_STOPS_LEVEL);
               //--- value normalizing
               sl=NormalizeDouble(sl,m_digits);
               //--- value normalizing
               minimal=NormalizeDouble(minimal,m_digits);
               //--- if unable to place Stop Loss on level, obtained from indicator, 
               //    this Stop Loss will be placed on closest possible level
               sl=MathMin(sl,minimal);
               //--- value of Stop Loss position
               double possl=PositionGetDouble(POSITION_SL);
               //--- value normalizing
               possl=NormalizeDouble(possl,m_digits);
               //--- if new value of Stop Loss if bigger than current value of Stop Loss, 
               //    an attempt to move Stop Loss on a new level will be made
               if(sl>possl)
                 {
                  //--- filling request structure
                  m_request.sl=sl;
                  //--- filling request structure
                  m_request.tp=PositionGetDouble(POSITION_TP);
                  //--- request
                  OrderSend(m_request,m_result);
                  //--- check request result
                  if(m_result.retcode!=TRADE_RETCODE_DONE)
                    {
                     //--- log error message
                     printf("Unable to move Stop Loss of position %s, error #%I64u",m_symbol,m_result.retcode);
                     //--- unable to move Stop Loss, finishing
                     return(false);
                    }
                 }
              }
           }
         break;
         //--- Down trend
      case -1:
         if(PositionSelect(m_symbol))
           {
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
              {
               sl=SellStoploss();
               //--- adding spread, since Sell is closing by the Ask price
               sl+=(SymbolInfoDouble(m_symbol,SYMBOL_ASK)-SymbolInfoDouble(m_symbol,SYMBOL_BID));
               double minimal=SymbolInfoDouble(m_symbol,SYMBOL_ASK)+m_point*SymbolInfoInteger(m_symbol,SYMBOL_TRADE_STOPS_LEVEL);
               sl=NormalizeDouble(sl,m_digits);
               minimal=NormalizeDouble(minimal,m_digits);
               sl=MathMax(sl,minimal);
               double possl=PositionGetDouble(POSITION_SL);
               possl=NormalizeDouble(possl,m_digits);
               if(sl<possl ||="" possl="=0)
                 {
                  m_request.sl=sl;
                  m_request.tp=PositionGetDouble(POSITION_TP);
                  OrderSend(m_request,m_result);
                  if(m_result.retcode!=TRADE_RETCODE_DONE)
                    {
                     printf("Unable to move Stop Loss of position %s, error #%I64u",m_symbol,m_result.retcode);
                     return(false);
                    }
                 }
              }
           }
         break;
     }
//--- remember the time of method's last execution
   m_lasttime=tm[0];
   return(true);
  }

Note as diferenças no código para as situações de compra e venda. Para a posição de venda, o valor retornado por SellStopLoss é aumentado pelo valor da dispersão, porque a posição de venda é fechada pelo Preço de Venda. Portanto, a contagem regressiva do nível mínimo de Stop Loss (Parar Perdas) para compra é feita pelo preço de Compra, para a Venda - a partir do Preço de Venda.

Por agora nós encerramos com a criação de classes bases de parada móvel. Vamos proceder com a criação de subclasses.

2. Subclasse de parada móvel para indicador Parabolic SAR

Os métodos virtuais de classe CTraillingStop já te dizem o conteúdo de uma subclasse - Os métodos SetParameters(), Refresh(), Tren(), BuyStoploss(), SellStoploss() e o construtor de classe para configurar o nome da parada móvel. Esta classe será nomeada como CParabolicStop. Uma vez que a classe é uma subclasse de CTraillingStop, isso será mencionado em sua declaração.

class CParabolicStop: public CTrailingStop

Com essa declaração, a nomeação de métodos virtuais de classes CParabolicStop executará métodos herdades da classe base.

Vamos considerar em detalhe todos os métodos da subclasse.

2.1. Método CParabolicStop()

Este método tem o mesmo nome da classe em si, este método é chamado de construtor. É executado automaticamente quando uma classe é carregada, antes de qualquer outro métodos de classe. No método CParabolicStop() o nome do limite é atribuído a variável m_typername. Esta variável é utilizada para criar o nome e título do botão (no método Init() da classe base).

void CParabolicStop()
  {
   m_typename="SAR"; // setting name of trailing stop type
  };

2.2. Método SetParameters()

Quando denominado o método SetParameters() ele aceita os parâmetros indicadores, e indicador é carregado com estes parâmetros. Se o parâmetro m_indicator é configurado no método Init() da classe base, então o indicador é anexado a planilha (função ChartIndicatorAdd()).

// Method of setting parameters and loading the indicator
bool SetParameters(double sarstep=0.02,double sarmaximum=0.2)
  {
   m_handle=iSAR(m_symbol,m_timeframe,sarstep,sarmaximum); // loading indicator
   if(m_handle==-1)
     {
      return(false); // if unable to load indicator, method returns false
     }
   if(m_indicator)
     {
      ChartIndicatorAdd(0,0,m_handle); // attach indicator to chart
     }
    
   return(true);
  }

2.3. Método Refresh()

O método Refresh() obtém novos preços e atualiza os valores indicadores. Na seção "protegida" da classe, existe o arranjo pricebuf para valor de preço e o arranjo indbuf - para valores indicadores. Ambos ensaios tem tamanho de um elemento - devem ser apenas um valor de preço e um valor de indicador para a barra formada ou em formação (dependendo do parâmetro m_shift, configure na inicialização da classe base).

// Method of getting indicator values
bool Refresh()
  {
   if(CopyBuffer(m_handle,0,m_shift,1,indbuf)==-1)
     {
      return(false); // if unable to copy value to array, return false
     }
    
   if(CopyClose(m_symbol,m_timeframe,m_shift,1,pricebuf)==-1)
     {
      return(false); // if unable to copy value to array, return false
     }    
   return(true); 
  }

2.4. Método Trend()

O método Trend() checa a localização do preço em relação a linha indicadora. Se o preço está acima da linha, então esta é uma média acima, e o método retorna o valor 1. Se o preço está abaixo da linha, então esta é uma média abaixo, e o método retorna o valor -1. Não excluído o caso (raro, mas possível), quando o preço é igual a linha indicadora. Neste caso, o valor 0 será retornado.

// Method of finding trend
int Trend()
  {
   if(pricebuf[0]>indbuf[0])
     { // price is higher than indicator line, up trend
      return(1);
     }
   if(pricebuf[0]<indbuf[0])
     { // price is lower than indicator line, down trend
      return(-1);
     }    
   return(0);
  }

2.5. Métodos BuyStoploss() e SellStoplss()

Uma vez que o parâmetro Parabolic SAR tem apenas uma linha, ambos os métodos são idênticos. Eles retornam o valor obtido do método Refresh().

// Method of finding out Stop Loss level for buy
virtual double BuyStoploss()
  {
   return(indbuf[0]);
  };
// Method of finding out Stop Loss level for sell
virtual double SellStoploss()
  {
   return(indbuf[0]);
  };

Por agora, a parada móvel está pronta. Possui apenas uma classe por enquanto, mas já pode ser usado. Salve-o como um arquivo incluído separadamente na pasta .\MQL5\Include sob o nome Sample_TrailingStop.mqh (o arquivo esta anexado ao artigo).

3. Adicionando a parada móvel para Parabolic no Especialista

Vamos tentar adicionar parada móvel em algum especialista, como My_First_EA do artigo Step-By-Step Guide to writing an Expert Advisor in MQL5 for Beginners.

3.1. Abra o especialista My_First_EA no MetaEditor e salve-o como My_First_EA_SARTrailing.

3.2. Inclua o arquivo da parada móvel. Na parte superior do código do especialista (preferencialmente antes da declaração de variáveis externas) adicione a linha:

#include  // include Trailing Stop class

3.3. Após, as variáveis externas criam uma instância da classe CParabolicStop, nomeada como Trailling.

CParabolicStop Trailing; // create class instance 

3.4. Na função OnInit() inicialize a classe e configure os seus parâmetros. Primeiro, declare as variáveis externas com parâmetros indicadores:

input double TrailingSARStep=0.02;
input double TrailingSARMaximum=0.2;

Então adicione o código a função OnInit().

Trailing.Init(_Symbol,PERIOD_CURRENT,true,true,false); // Initialize (set basic parameters)
if(!trailing.setparameters(TrailingSARStep,TrailingSARMaximum))
  { // Set parameters of used trailing stop type
   Alert("trailing error");
   return(-1);
  } 
Trailing.StartTimer(); // Start timer
Trailing.On();         // Turn on

3.5. No código do especialista ache a função OnTimer(). A função OnTimer() não é utilizada no My_First_EA, então adicione-a, e nela insira chamada do Refresh().

void OnTimer()
  {
   Trailing.Refresh();
  }

3.6. No topo da função OnTick() adicionei a nomeação do método DoStopLoss().

3.7. Compile o especialista e tente testá-lo. Os resultados do teste do especialista são demonstrados na Figura 7 (sem parada móvel) e na figura 8 (com parada móvel).

Figura 7. Resultados do Teste do Especialista sem Parada Móvel.

Figura 7. Resultados do Teste do Especialista sem Parada Móvel

Figura 8. Resultados do Teste do Especialista com Parada Móvel.

Figura 8. Resultados do Teste do Especialista com Parada Móvel

A efetividade do uso da parada móvel é óbvia.

O arquivo My_First_EA_SARTrailing.mq5 está anexado ao artigo.

4. Subclasse de parada móvel para NRTR

O indicador NRTR (Nick Rypock Trailling Reverse) pelo seu nome e aparição tem algo interessante para tentar criar nele uma parada móvel.

Figura 9. Indicador NRTR.

Figura 9. Indicador NRTR

O indicador traça a linha base (a linha de suporte ou resistência) e a linha alvo. Quando o preço excede a linha alvo, a linha base é transferida na direção da movimento do preço, pequenas oscilações de preço são ignoradas. Quando o preço cruza a linha base - é considerado como uma mudança de média, assim, mudando a localização da linha base e da linha alvo, de acordo com o preço. Linha suporte e linha alvo na média superior são pintadas com azul, e na média inferior - com vermelho.

Crie outra parada móvel, agora para o indicador NRTR.

Declare outra classe CNRTRStop inclusa na classe base CNRTRStop.

class CNRTRStop: public CTrailingStop

A Parada Móvel para a subclasse NRTR terá exatamente o mesmo conjunto de métodos como a Parada Móvel para Parabolic, exceto o construtor - agora ele será chamado de CNRTRStop().

4.1. Método CNRTRStop()

Agora no construtor de classe a variável m_typername é atribuída com valor NRTR, de acordo com o indicador usado.

void CNRTRStop()
  {
   m_typename="NRTR"; // setting name of trailing stop type
  };

4.2. Método SetParameters()

Quando denominado o método SetParameters() ele aceita os parâmetros indicadores, e é carregado. Então, dependendo dos parâmetros básicos de parada móvel, o indicador será anexado à planilha.

// Method of setting parameters and loading the indicator
bool SetParameters(int period,double k)
  {
   m_handle=iCustom(m_symbol,m_timeframe,"NRTR",period,k); // loading indicator
   if(m_handle==-1)
     { // if unable to load indicator, method returns false
      return(false); 
     }
   if(m_indicator)
     {
       
      ChartIndicatorAdd(0,0,m_handle); // attach indicator to chart
     }
   return(true);
  }

4.3. Método Refresh()

O método Refresh() copia tampões do indicador NRTR - tampão da linha suporte e tampão da linha de resistência.

 // Method of getting indicator values
bool Refresh()
  {
   if(CopyBuffer(m_handle,0,m_shift,1,sup)==-1)
     {
      return(false); // if unable to copy value to array, return false
     }
    
   if(CopyBuffer(m_handle,1,m_shift,1,res)==-1)
     {
      return(false); // if unable to copy value to array, return false
     }
    
   return(true);
  }

Na seção "protected" da classe, existem dois arranjos declarados: double sup[] and double res[].

protected:
double sup[1]; // value of support level
double res[1]; // value of resistance level

4.4. Método Trend()

O método Trend() checa quais linhas realmente existem. Se é a linha suporte, isto significa que o indicador demonstra a média superior. O método em si retorna o valor 1. Se é a linha de resistência, o método retorna -1.

// Method of finding trend
int Trend()
  {
   if(sup[0]!=0)
     { // there is support line, then it is up trend
      return(1);
     }
   if(res[0]!=0)
     { // there is resistance line, then it is down trend
      return(-1);
     }
    
   return(0);
  }

4.5. Método BuyStoploss()

O método BuyStoploss() retorna o valor da linha suporte.

// Method of finding out Stop Loss level for buy
double BuyStoploss()
  {
   return(sup[0]);
  }

4.6. Método SellStoploss()

O método BuyStoploss() retorna o valor da linha suporte.

// Method of finding out Stop Loss level for sell
double SellStoploss()
  {
   return(res[0]);
  }

Agora a classe da parada móvel está completa.

5. Adicionando Parada Móvel para NRTR no Especialista

Assim como a parada móvel para Parabolic, vamos adicionar a parada móvel My_First_EA para o NRTR no especialista.

5.1. Abra o especialista My_First_EA_SARTrailing no MetaEditor e salve-o como My_First_EA_NRTRTrailing.

5.2. Substitua os parâmetros externos da parada móvel para Parabolic com os parâmetros da parada móvel para NRTR.

input int TrailingNRTRPeriod = 40;
input double TrailingNRTRK   =  2;

5.3. Ao invés de criar uma instância da classe CParabolicStop, crie uma instância da classe CNRTRSTop. O código é localizado após as variáveis externas.

CNRTRStop Trailing; // create class instance 

5.4. Na função OnInit() substitua parâmetros do método SetParameters() com os parâmetros NRTR.

Trailing.SetParameters(TrailingNRTRPeriod,TrailingNRTRK)

5.5. Compile o especialista e tente testá-lo.

Figura 10. Resultados do Teste do Especialista com Parada Móvel para NRTR.

Figura 10. Resultados do Teste do Especialista com Parada Móvel para NRTR

Os resultados funcionais do Consultor Especialista (Figura 10) com uma estratégia de parada móvel, comparadas com o trabalho de um especialista sem este limite, é praticamente idêntica. Uso de parada móvel para Parabolic provou-se mais eficiente para este especialista. Pode ser concluído que o arsenal de certos números de parada móvel pode ser bastante útil quando desenvolvendo especialistas - para fazer experimentos e selecionar os tipos mais adequados de limites móveis.

O arquivo My_First_EA_NRTRTrailing.mq5 está anexado ao artigo.

6. Assistência Especialista

Quando uma classe base de parada móvel foi criada, a intenção foi controlar a opção de ligar/desligar o parada móvel via botão. Vamos criar um especialista assistente para acompanhar situações nos vários símbolos com diferentes tipos de parada móvel. O especialista não abrirá situações, mas apenas acompanhará as já abertas.

6.1. No MetaEditor crie novo especialista chamado Sample_TrailingStop.

6.2. Inclua o arquivo Sample_TrailingStop.mqh.

#include  // include Trailing Stop class

6.3. Declare parâmetros externos para indicadores.

input double SARStep=0.02;     // Step of Parabolic
input double SARMaximum=0.02;  // Maximum of Parabolic
input int NRTRPeriod=40;       // NRTR period
input double NRTRK=2;          // NRTR factor

6.4. Declare arranjo de símbolos, em qual o especialista será capaz de trabalhar.

string Symbols[]={"EURUSD","GBPUSD","USDCHF","USDJPY"};

6.5. Declare arranjos para carregar classes.

CParabolicStop *SARTrailing[];
CNRTRStop *NRTRTrailing[];

6.6. Na função OnInit() redimensione arranjos para carregar classes, de acordo com o tamanho de cada arranjo de símbolos.

ArrayResize(SARTrailing,ArraySize(Symbols));  // resize according to number of used symbols
ArrayResize(NRTRTrailing,ArraySize(Symbols)); // resize according to number of used symbols 

6.7. No loop para cada elemento de arranjo carregue instâncias de classes.

for(int i=0;i<ArraySize(Symbols);i++)
  { // for all symbols
   SARTrailing[i]=new CParabolicStop(); // create CParabolicStop class instance
   SARTrailing[i].Init(Symbols[i],PERIOD_CURRENT,false,true,true,5,15+i*17,Silver,Blue);    // initialization of CParabolicStop class instance 
   if(!SARTrailing[i].SetParameters(SARStep,SARMaximum))
     { // setting parameters of CParabolicStop class instance 
      Alert("trailing error");
      return(-1);
     }
   SARTrailing[i].StartTimer();         // start timer
//----
   NRTRTrailing[i]=new CNRTRStop();     // create CNRTRStop class instance
   NRTRTrailing[i].Init(Symbols[i],PERIOD_CURRENT,false,true,true,127,15+i*17,Silver,Blue); // initialization of CNRTRStop class instance
   if(!NRTRTrailing[i].SetParameters(NRTRPeriod,NRTRK))
     { // setting parameters of CNRTRcStop class instance 
      Alert("trailing error");
      return(-1);
     }
   NRTRTrailing[i].StartTimer();        // start timer 
  }

Nota: ao denominar os botões do método Init(), coordenadas são calculadas. Na esquerda haverão botões para ligar as paradas móveis para Parabolic, na direita - para NRTR.

6.8. Na função OnTick() adicione a nomeação do método DoStoploss() para cada instância de parada móvel.

for(int i=0;i<ArraySize(Symbols);i++)
  {
   SARTrailing[i].DoStoploss();
   NRTRTrailing[i].DoStoploss();
  }

6.9. Adicione controle de planilha de eventos.

void OnChartEvent(const int         id,
                  const long   &lparam,
                  const double &dparam,
                  const string &sparam 
                  )
  {
   for(int i=0;i<ArraySize(Symbols);i++)
     {
      SARTrailing[i].EventHandle(id,lparam,dparam,sparam);
      NRTRTrailing[i].EventHandle(id,lparam,dparam,sparam);
     }
    
  }

6.10. Na função Deinit() desinicialize todas as instâncias de classes e as delete.

for(int i=0;i<ArraySize(Symbols);i++)
  {
   SARTrailing[i].Deinit(); 
   NRTRTrailing[i].Deinit();
   delete(SARTrailing[i]);  // delete object
   delete(NRTRTrailing[i]); // delete object
  }

Compile, anexe o especialista a planilha. Indicadores e botões aparecem na planilha (Figura 11) - Expert Advisor esta pronto para funcionar.

Figura 11. Botões e Indicadores na Planilha Após Iniciar o Sample_TrailingStop.

Figura 11. Botões e Indicadores na Planilha Após Iniciar o Sample_TrailingStop

Apenas pressione o botão para seguir para a posição correspondente quando este é aberto.

O arquivo Sample_TrailingStop.mq5 está anexado ao artigo.

Conclusão

Vamos revisar a ordem de uso da classe CTraillingStop quando usando um sistema de negociações manual:

1. Inclua o arquivo Sample_TrailingStop.mqh.

2. Declare variáveis externas com parâmetros indicadores de parada móvel utilizado.

3. Crie a instância da classe.

4. Adicione nomeação dos métodos Init(), SetParameters(), StartTimer() e On() e da função OnInit().

5. Adicione nomeação do método Refresh() a partir da função OnTimer().

6. Adicione nomeação do método DoStopLoss() a partir da função OnTick().

7. Adicione nomeação do método Deinit() a partir da função OnDeinit().


Sete passos, menos de cinco minutos, e seu Consultor Especialista tem a função de parada móvel!

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/134

Criando indicadores multicoloridos no MQL5 Criando indicadores multicoloridos no MQL5
Neste artigo, consideraremos como criar indicadores multicoloridos ou converter os existentes para multicor. O MQL5 permite representar as informações de forma conveniente. Agora, não é necessário ver uma dúzia de gráficos com indicadores e realizar análises dos níveis RSI ou estocásticos, é melhor apenas pintar as velas com cores diferentes dependendo dos valores dos indicadores.
O protótipo do robô de negócio O protótipo do robô de negócio
Este artigo resume e sintetiza os princípios da criação de algoritmos e elementos dos sistemas de negócio. O artigo considera o planejamento do algoritmo de especialista. Como um exemplo, a classe CExpertAdvisor é considerada, o que pode ser usado para facilmente e rapidamente desenvolver sistemas de negócio.
Diversas maneiras de se encontrar uma tendência no MQL5 Diversas maneiras de se encontrar uma tendência no MQL5
Qualquer comerciante daria muito pela oportunidade de detectar precisamente uma tendência em qualquer dado momento. Talvez, este seja o Santo Graal que todo mundo esteja procurando por. Este artigo considerará diversas maneiras de detectar uma tendência. Para ser mais preciso - como programar diversas maneiras clássicas para detectar uma tendência pelos meios do MQL5.
20 sinais de negociação no MQL5 20 sinais de negociação no MQL5
Este artigo o ensinará a como receber os sinais de negócio que são necessários para um sistema de negócio funcionar. O exemplo de formação de 20 sinais de negócio é forncedio aqui como funções de personalização separadas que podem ser usadas ao desenvolver Expert Advisors. Para sua conveniência, todas as funções utilizadas no artigo são combinadas em um único arquivo mqh que pode ser facilmente conectado a um futuro Expert Advisor.