Guia prático do MQL5: Utilizando indicadores para ajustar condições de negócios no Consultor Especialista

Anatoli Kazharski | 21 março, 2014

Introdução

Neste artigo, o Consultor Especialista será melhorado com indicadores dos quais os valores serão usados para verificar as condições de abertura de posição. Para aprimorá-lo, criaremos uma lista de menu suspenso nos parâmetros externos para ser possível selecionar um de três indicadores de transações.

Por favor, lembre-se: continuamos a modificar o Consultor Especialista que estamos trabalhando durante os artigos precedentes da série Guia prático do MQL5. A última versão do Consultor Especialista pode ser baixada do artigo intitulado "Guia prático do MQL5: O histórico de transações e biblioteca de função para obter propriedades de posição".

Adicionalmente, este artigo apresentará uma função que criaremos para verificar se operações de negócios podem ou não ser realizadas. A função de abertura de posição será modificada para ativar o Consultor Especialista para determinar o modo de negociação (Execução Instantânea e Execução de Mercado).

Já que o código do Consultor Especialista, seguido de todas as melhorias feitas nos artigos anteriores já excede 1.500 linhas, será cada vez menos prático, com cada novo recurso adicionado. Portanto, a solução lógica é dividí-lo em várias categorias como arquivos de bibliotecas separados. Agora que nossos objetivos foram definidos, vamos começar.

 

Desenvolvimento do Consultor Especialista

Colocamos o código fonte do Consultor Especialista (*.mq5) do artigo anterior em uma pasta separada, TestIndicatorConditionobs, na qual precisamos criar a subpasta Include. Esta é a pasta onde criaremos os arquivos include (*.mqh). Eles podem ser gerados usando o Wizard MQL5 (Ctrl+N) ou criados manualmente no diretório requerido como arquivos de texto padrão (*.txt) e depois renomeados para *.mqh.

Abaixo estão os nomes e comentários para todos os arquivos include criados:

Para incluir estas bibliotecas no arquivo principal, usamos a diretiva #include. Como o principal arquivo do Consultor Especialista e a pasta de arquivos include (Include) estão localizadas na mesma pasta, o código para incluir arquivos será como se segue:

//--- Include custom libraries
#include "Include\Enums.mqh"
#include "Include\InfoPanel.mqh"
#include "Include\Errors.mqh"
#include "Include\TradeSignals.mqh"
#include "Include\TradeFunctions.mqh"
#include "Include\ToString.mqh"
#include "Include\Auxiliary.mqh"

Então, seremos capazes de abrí-los e modificá-los e mover uma parte do código-fonte do arquivo principal do Consultor Especialista.

Para navegar corretamente pelo código, referências aos arquivos de cabeçalho adjacentes, assim como ao arquivo principal do Consultor Especialista serão adicionados a cada arquivo de cabeçalho. Por exemplo, para nossa biblioteca de funções de negociação TradeFunctions.mqh, vai parecer desta forma:

//--- Connection with the main file of the Expert Advisor
#include "..\TestIndicatorConditions.mq5"
//--- Include custom libraries
#include "Enums.mqh"
#include "InfoPanel.mqh"
#include "Errors.mqh"
#include "TradeSignals.mqh"
#include "ToString.mqh"
#include "Auxiliary.mqh"

Para arquivos no mesmo nível de aninhamento, é suficiente especificar simplesmente o nome. Para subir um nível, você precisa colocar dois pontos antes do traço preto no caminho.

Vamos adicionar enumeração para indicadores no arquivo Enums.mqh. Para propósitos ilustrativos, neste Consultor Especialista usaremos dois indicadores padrão (Média Móvel e Índice de Canal de Commodity) e um indicador personalizado (MultiRange_PCH). A enumeração será como se segue:

//--- Indicators
enum ENUM_INDICATORS
  {
   MA       = 0, // Moving Average
   CCI      = 1, // CCI
   PCH      = 2  // Price Channel
  };

Os parâmetros externos são modificados como se segue:

//--- External parameters of the Expert Advisor
sinput   long              MagicNumber=777;        // Magic number
sinput   int               Deviation=10;           // Slippage
input    ENUM_INDICATORS   Indicator=MA;           // Indicator
input    int               IndicatorPeriod=5;      // Indicator period
input    int               IndicatorSegments=2;    // Number of one direction indicator segments
input    double            Lot=0.1;                // Lot
input    double            VolumeIncrease=0.1;     // Position volume increase
input    double            VolumeIncreaseStep=10;  // Step for volume increase
input    double            StopLoss=50;            // Stop Loss
input    double            TakeProfit=100;         // Take Profit
input    double            TrailingStop=10;        // Trailing Stop
input    bool              Reverse=true;           // Position reversal
sinput   bool              ShowInfoPanel=true;     // Display of the info panel

Como mencionado acima, você será capaz de selecionar um de três indicadores na lista do menu suspenso do parâmetro Indicador.

Há apenas um parâmetro aplicável a todos indicadores onde o período do indicador pode ser definido - IndicatorPeriod. O parâmetro NumberOfBars da versão anterior do Consultor Especialista foi renomeado para IndicatorSegments e agora denota o número de barras durante os quais um dado indicador precisa ir para cima/baixo para satisfazer a condição de abertura de posição.

Além disso, adicionamos outro parâmetro externo, VolumeIncreaseStep, que pode ser usado para definir o passo para o aumento do volume em pontos.

O valor da variável AllowedNumberOfBars (agora AllowedNumberOfSegments) usada para ser ajustada na função personalizada GetBarsData(). Agora será colocada em uma função separada e ser chamada apenas na inicialização.

Já que a condição de abertura de posição será agora verificada usando valores do indicador, o valor a ser designado será sempre maior por dois. Em outras palavras, se a variável externa IndicatorSegments é designada o valor de 1, a variável AllowedNumberOfSegments será designado o valor 3, já que a ordem para satisfazer a condição (Por exemplo para uma COMPRA), o valor indicador na barra completada deve ser mais do que aquele na barra anterior. Para este propósito, precisamos obter os últimos três valores de indicador.

Abaixo está o código da função CorrectInputParameters():

//+------------------------------------------------------------------+
//| Adjusting input parameters                                       |
//+------------------------------------------------------------------+
void CorrectInputParameters()
  {
//--- Adjust the number of bars for the position opening condition
   if(AllowedNumberOfSegments<=0)
     {
      if(IndicatorSegments<=1)
         AllowedNumberOfSegments=3;                     // At least three bars are required
      if(IndicatorSegments>=5)
         AllowedNumberOfSegments=5;                     // but no more than 7
      else
         AllowedNumberOfSegments=IndicatorSegments+1;   // and always greater by two
     }
  }

Antes de começarmos a negociar com os indicadores, vamos criar uma função que verificará se negociar é permitido - CheckTradingPermission(). Se negociar não é permitido por quaisquer razões listadas na função, o valor zero será retornado. Isto significará que a próxima tentativa terá de ser feita na próxima barra.

//+------------------------------------------------------------------+
//| Checking if trading is allowed                                   |
//+------------------------------------------------------------------+
bool CheckTradingPermission()
  {
//--- For real-time mode
   if(IsRealtime())
     {
      //--- Checking server connection
      if(!TerminalInfoInteger(TERMINAL_CONNECTED))
         return(1);
      //--- Permission to trade at the running program level
      if(!MQL5InfoInteger(MQL5_TRADE_ALLOWED))
         return(2);
      //--- Permission to trade at the terminal level
      if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
         return(3);
      //--- Permission to trade for the current account
      if(!AccountInfoInteger(ACCOUNT_TRADE_ALLOWED))
         return(4);
      //--- Permission to trade automatically for the current account
      if(!AccountInfoInteger(ACCOUNT_TRADE_EXPERT))
         return(5);
     }
//---
   return(0);
  }

Vamos agora ao ponto principal do artigo. Para obter acesso aos valores do indicador, precisamos primeiro obter seu manipulador. Isto é feito usando funções especiais cujos nomes são feitos do nome curto do indicador e o símbolo 'i' precedendo-o.

Por exemplo, o indicador de Média Móvel tem a função correspondente iMA(). Todos os manipuladores de indicadores no terminal MetaTrader 5 podem ser obtidos usando estas funções. A lista completa está disponível na seção de Referência MQL5 chamada Indicadores Técnicos. Se você precisa obter um manipulador de um indicador personalizado, use a função iCustom().

Implementaremos a função GetIndicatorHandle() onde dependendo do indicador selecionar no parâmetro Indicador o valor do manipulador do indicador correspondente será designado para a variável global indicator_handle. O código da função pode ser encontrado na nossa biblioteca de sinais de funções de negociação (o arquivo \Include\TradeSignals.mqh), enquanto a variável com o manipulador do indicador está localizada no arquivo principal do Consultor Especialista.

//+------------------------------------------------------------------+
//| Getting the indicator handle                                     |
//+------------------------------------------------------------------+
void GetIndicatorHandle()
  {
//--- If the Moving Average indicator is selected
   if(Indicator==MA)
      indicator_handle=iMA(_Symbol,Period(),IndicatorPeriod,0,MODE_SMA,PRICE_CLOSE);
//--- If the CCI indicator is selected
   if(Indicator==CCI)
      indicator_handle=iCCI(_Symbol,Period(),IndicatorPeriod,PRICE_CLOSE);
//--- If the MultiRange_PCH indicator is selected
   if(Indicator==PCH)
      indicator_handle=iCustom(_Symbol,Period(),"MultiRange_PCH",IndicatorPeriod);
//--- If the indicator handle could not be obtained
   if(indicator_handle==INVALID_HANDLE)
      Print("Failed to get the indicator handle!");
  }

Além disso, criamos a função GetDataIndicators() onde usando os manipuladores de indicador obtidos, podemos obter seus valores. Isto é feito usando a função CopyBuffer() de forma similar ao obter valores da barra usando as funções CopyTime(), CopyClose(), CopyOpen(), CopyHigh() e CopyLow() consideradas no artigo "Guia prático do MQL5: Analisando Propriedades de Posição no Testador de Estratégia MetaTrader 5".

Já que o indicador pode ter várias reservas (linhas de valores), o índice de reservas é passado para a função CopyBuffer() como o segundo parâmetro. Índices de reserva para indicadores padrão podem ser encontrados na Referência MQL5. Para indicadores personalizados, os índices de reserva podem ser encontrados no código, desde que o código-fonte esteja disponível. Se não há código, você precisará encontrar o índice pela forma de experimento, observando como as condições são satisfeitas no modo de visualização do Testador de Estratégia.

Antes disso, precisamos criar matrizes dinâmicas para valores de reserva de indicador no arquivo principal do Consultor Especialista.

//--- Arrays for indicator values
double indicator_buffer1[];
double indicator_buffer2[];

O código da GetIndicatorsData() é dado abaixo:

//+------------------------------------------------------------------+
//| Getting indicator values                                         |
//+------------------------------------------------------------------+
bool GetIndicatorsData()
  {
//--- If the indicator handle has been obtained
   if(indicator_handle!=INVALID_HANDLE)
     {
      //--- For the Moving Average or CCI indicator
      if(Indicator==MA || Indicator==CCI)
        {
         //--- Reverse the indexing order (... 3 2 1 0)
         ArraySetAsSeries(indicator_buffer1,true);
         //--- Get indicator values
         if(CopyBuffer(indicator_handle,0,0,AllowedNumberOfSegments,indicator_buffer1)<allowednumberofsegments)           ="" {=""             Print("Failed to copy the values ("+
                  _Symbol+"; "+TimeframeToString(Period())+") to the indicator_buffer1 array! Error ("+
                  IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError()));
            return(false);
           }
        }
      //--- For the MultiRange_PCH indicator
      if(Indicator==PCH)
        {
         //--- Reverse the indexing order (... 3 2 1 0)
         ArraySetAsSeries(indicator_buffer1,true);
         ArraySetAsSeries(indicator_buffer2,true);
         //--- Get indicator values
         if(CopyBuffer(indicator_handle,0,0,AllowedNumberOfSegments,indicator_buffer1)<allowednumberofsegments ||=""             CopyBuffer(indicator_handle,1,0,AllowedNumberOfSegments,indicator_buffer2)<allowednumberofsegments)           ="" {=""             Print("Failed to copy the values ("+
                  _Symbol+"; "+TimeframeToString(Period())+") to the indicator_buffer1 or indicator_buffer2 array! Error ("+
                  IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError()));
            return(false);
           }
        }
      //---
      return(true);
     }
//--- If the indicator handle has not been obtained, retry
   else
      GetIndicatorHandle();
//---
   return(false);
  }

A função GetTradingSignal() mudou substancialmente. As condições são diferentes na ausência da posição e onde a posição existe. Para os indicadores da Média Móvel e CCI, as condições são as mesmas. Para MultiRange_PCH, estão dispostas em um bloco separado. Para fazer o código mais legível e evitar repetições, criamos uma função auxiliar, GetSignal(), que retorna um final para abertura de posição ou reversão, desde que tal posição exista e a ação relevante é permitida pelo parâmetro externo.

Abaixo está o código da função GetSignal():

//+------------------------------------------------------------------+
//| Checking the condition and returning a signal                    |
//+------------------------------------------------------------------+
ENUM_ORDER_TYPE GetSignal()
  {
//--- Check conditions for the Moving Average and CCI indicators
   if(Indicator==MA || Indicator==CCI)
     {
      //--- A Sell signal
      if(AllowedNumberOfSegments==3 && 
         indicator_buffer1[1]<indicator_buffer1[2])
         return(ORDER_TYPE_SELL);
      //---
      if(AllowedNumberOfSegments==4 && 
         indicator_buffer1[1]<indicator_buffer1[2] && 
         indicator_buffer1[2]<indicator_buffer1[3])
         return(ORDER_TYPE_SELL);
      //---
      if(AllowedNumberOfSegments==5 && 
         indicator_buffer1[1]<indicator_buffer1[2] && 
         indicator_buffer1[2]<indicator_buffer1[3] && 
         indicator_buffer1[3]<indicator_buffer1[4])
         return(ORDER_TYPE_SELL);
      //---
      if(AllowedNumberOfSegments==6 && 
         indicator_buffer1[1]<indicator_buffer1[2] && 
         indicator_buffer1[2]<indicator_buffer1[3] && 
         indicator_buffer1[3]<indicator_buffer1[4] && 
         indicator_buffer1[4]<indicator_buffer1[5])
         return(ORDER_TYPE_SELL);
      //---
      if(AllowedNumberOfSegments>=7 && 
         indicator_buffer1[1]<indicator_buffer1[2] && 
         indicator_buffer1[2]<indicator_buffer1[3] && 
         indicator_buffer1[3]<indicator_buffer1[4] && 
         indicator_buffer1[4]<indicator_buffer1[5] && 
         indicator_buffer1[5]<indicator_buffer1[6])
         return(ORDER_TYPE_SELL);

      //--- A Buy signal
      if(AllowedNumberOfSegments==3 && 
         indicator_buffer1[1]>indicator_buffer1[2])
         return(ORDER_TYPE_BUY);
      //---
      if(AllowedNumberOfSegments==4 && 
         indicator_buffer1[1]>indicator_buffer1[2] && 
         indicator_buffer1[2]>indicator_buffer1[3])
         return(ORDER_TYPE_BUY);
      //---
      if(AllowedNumberOfSegments==5 && 
         indicator_buffer1[1]>indicator_buffer1[2] && 
         indicator_buffer1[2]>indicator_buffer1[3] && 
         indicator_buffer1[3]>indicator_buffer1[4])
         return(ORDER_TYPE_BUY);
      //---
      if(AllowedNumberOfSegments==6 && 
         indicator_buffer1[1]>indicator_buffer1[2] && 
         indicator_buffer1[2]>indicator_buffer1[3] && 
         indicator_buffer1[3]>indicator_buffer1[4] && 
         indicator_buffer1[4]>indicator_buffer1[5])
         return(ORDER_TYPE_BUY);
      //---
      if(AllowedNumberOfSegments>=7 && 
         indicator_buffer1[1]>indicator_buffer1[2] && 
         indicator_buffer1[2]>indicator_buffer1[3] && 
         indicator_buffer1[3]>indicator_buffer1[4] && 
         indicator_buffer1[4]>indicator_buffer1[5] && 
         indicator_buffer1[5]>indicator_buffer1[6])
         return(ORDER_TYPE_BUY);
     }
//--- Block that checks conditions for the MultiRange_PCH indicator
   if(Indicator==PCH)
     {
      //--- A Sell signal
      if(close_price[1]<indicator_buffer2[1] && 
         open_price[1]>indicator_buffer2[1])
         return(ORDER_TYPE_SELL);
      //--- A Buy signal
      if(close_price[1]>indicator_buffer1[1] && 
         open_price[1]<indicator_buffer1[1])
         return(ORDER_TYPE_BUY);
     }
//--- No signal
   return(WRONG_VALUE);
  }</indicator_buffer1[</indicator_buffer2[</indicator_buffer1[</indicator_buffer1[</indicator_buffer1[</indicator_buffer1[</indicator_buffer1[</indicator_buffer1[</indicator_buffer1[</indicator_buffer1[</indicator_buffer1[</indicator_buffer1[</indicator_buffer1[</indicator_buffer1[</indicator_buffer1[</indicator_buffer1[</indicator_buffer1[

O código da função GetTradingSignal() está agora como se segue:

//+------------------------------------------------------------------+
//| Determining trading signals                                      |
//+------------------------------------------------------------------+
ENUM_ORDER_TYPE GetTradingSignal()
  {
//--- If there is no position
   if(!pos.exists)
     {
      //--- A Sell signal
      if(GetSignal()==ORDER_TYPE_SELL)
         return(ORDER_TYPE_SELL);
      //--- A Buy signal
      if(GetSignal()==ORDER_TYPE_BUY)
         return(ORDER_TYPE_BUY);
     }
//--- If the position exists
   if(pos.exists)
     {
      //--- Get the position type
      GetPositionProperties(P_TYPE);
      //--- Get the last deal price
      GetPositionProperties(P_PRICE_LAST_DEAL);
      //--- Block that checks conditions for the Moving Average and CCI indicators
      if(Indicator==MA || Indicator==CCI)
        {
         //--- A Sell signal
         if(pos.type==POSITION_TYPE_BUY && 
            GetSignal()==ORDER_TYPE_SELL)
            return(ORDER_TYPE_SELL);
         //---
         if(pos.type==POSITION_TYPE_SELL && 
            GetSignal()==ORDER_TYPE_SELL && 
            close_price[1]<pos.last_deal_price-correctvaluebysymboldigits(volumeincreasestep*_Point))
            return(ORDER_TYPE_SELL);
         //--- A Buy signal
         if(pos.type==POSITION_TYPE_SELL && 
            GetSignal()==ORDER_TYPE_BUY)
            return(ORDER_TYPE_BUY);
         //---
         if(pos.type==POSITION_TYPE_BUY && 
            GetSignal()==ORDER_TYPE_BUY && 
            close_price[1]>pos.last_deal_price+CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point))
            return(ORDER_TYPE_BUY);
        }
      //--- Block that checks conditions for the MultiRange_PCH indicator
      if(Indicator==PCH)
        {
         //--- A Sell signal
         if(pos.type==POSITION_TYPE_BUY && 
            close_price[1]<indicator_buffer2[1] && 
            open_price[1]>indicator_buffer2[1])
            return(ORDER_TYPE_SELL);
         //---
         if(pos.type==POSITION_TYPE_SELL && 

            close_price[1]<pos.last_deal_price-correctvaluebysymboldigits(volumeincreasestep*_Point))
            return(ORDER_TYPE_SELL);
         //--- A Buy signal
         if(pos.type==POSITION_TYPE_SELL && 
            close_price[1]>indicator_buffer1[1] && 
            open_price[1]<indicator_buffer1[1])
            return(ORDER_TYPE_BUY);
         //---
         if(pos.type==POSITION_TYPE_BUY && 
            close_price[1]>pos.last_deal_price+CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point))
            return(ORDER_TYPE_BUY);
        }
     }
//--- No signal
   return(WRONG_VALUE);
  }</indicator_buffer1[</pos.last_deal_price-correctvaluebysymboldigits(volumeincreasestep*</indicator_buffer2[</pos.last_deal_price-correctvaluebysymboldigits(volumeincreasestep*

Agora, precisamos apenas ver dentro dos modos Execução Imediata e Execução de Mercado que são parte das propriedades de símbolo para modificar a função de abertura de posição OpenPosition() de acordo. Os modos cujos nomes são autoexplicativos também podem ser encontrados na Referência MQL5:

Por favor, lembre-se que quando lidamos com o modo de Execução de Mercado, você não pode abrir uma posição com os níveis de Parar Perdas e Obter Lucros: você precisa primeiro abrir uma posição e então modificá-la, definindo seus níveis.

Começando com a versão 803, Parar Perdas e Obter Lucros podem ser definidos quando abrindo uma posição para os modos Execução de Mercado e Execução de Câmbio.

Vamos adicionar o modo de execução para a estrutura das propriedades de símbolo:

//--- Symbol properties
struct symbol_properties
  {
   int               digits;           // Number of decimal places in the price
   int               spread;           // Spread in points
   int               stops_level;      // Stops level
   double            point;            // Point value
   double            ask;              // Ask price
   double            bid;              // Bid price
   double            volume_min;       // Minimum volume for a deal
   double            volume_max;       // Maximum volume for a deal
   double            volume_limit;     // Maximum permissible volume for a position and orders in one direction
   double            volume_step;      // Minimum volume change step for a deal
   double            offset;           // Offset from the maximum possible price for a transaction
   double            up_level;         // Upper Stop level price
   double            down_level;       // Lower Stop level price
   ENUM_SYMBOL_TRADE_EXECUTION execution_mode; // Execution mode
  };

Consequentemente, precisamos modificar a enumeração ENUM_SYMBOL_PROPERTIES

//--- Enumeration of position properties
enum ENUM_SYMBOL_PROPERTIES
  {
   S_DIGITS          = 0,
   S_SPREAD          = 1,
   S_STOPSLEVEL      = 2,
   S_POINT           = 3,
   S_ASK             = 4,
   S_BID             = 5,
   S_VOLUME_MIN      = 6,
   S_VOLUME_MAX      = 7,
   S_VOLUME_LIMIT    = 8,
   S_VOLUME_STEP     = 9,
   S_FILTER          = 10,
   S_UP_LEVEL        = 11,
   S_DOWN_LEVEL      = 12,
   S_EXECUTION_MODE  = 13,
   S_ALL             = 14
  };

e a função GetSymbolProperties():

case S_EXECUTION_MODE: symb.execution_mode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE);   break;
      //---
      case S_ALL           :
         symb.digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         symb.spread=(int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD);
         symb.stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
         symb.point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
         symb.ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),symb.digits);
         symb.bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),symb.digits);
         symb.volume_min=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
         symb.volume_max=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
         symb.volume_limit=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT);
         symb.volume_step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
         symb.offset=NormalizeDouble(CorrectValueBySymbolDigits(lot_offset*symb.point),symb.digits);
         symb.up_level=NormalizeDouble(symb.ask+symb.stops_level*symb.point,symb.digits);
         symb.down_level=NormalizeDouble(symb.bid-symb.stops_level*symb.point,symb.digits);
         symb.execution_mode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE);                       break;
         //---

Como resultado, o código da função OpenPosition() é como se segue:

//+------------------------------------------------------------------+
//| Opening a position                                               |
//+------------------------------------------------------------------+
void OpenPosition(double lot,
                  ENUM_ORDER_TYPE order_type,
                  double price,
                  double sl,
                  double tp,
                  string comment)
  {
//--- Set the magic number in the trading structure
   trade.SetExpertMagicNumber(MagicNumber);
//--- Set the slippage in points
   trade.SetDeviationInPoints(CorrectValueBySymbolDigits(Deviation));
//--- The Instant Execution mode
//    A position can be opened with the Stop Loss and Take Profit levels set
   if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_INSTANT)
     {
      //--- If the position failed to open, print the relevant message
      if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment))
         Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
     }
//--- The Market Execution mode 
//    First open a position and only then set the Stop Loss and Take Profit levels
//    *** Starting with build 803, Stop Loss and Take Profit can be set upon position opening ***
   if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_MARKET)
     {
      //--- If there is no position, first open a position and then set Stop Loss and Take Profit
      if(!pos.exists)
        {
         //--- If the position failed to open, print the relevant message
         if(!trade.PositionOpen(_Symbol,order_type,lot,price,0,0,comment))
            Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
         //--- Get the flag of presence/absence of the position
         pos.exists=PositionSelect(_Symbol);
         //--- If the position exists
         if(pos.exists)
           {
            //--- Set Stop Loss and Take Profit
            if(!trade.PositionModify(_Symbol,sl,tp))
               Print("Error modifying the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
           }
        }
      //--- If the position exists, increase its volume and leave the Stop Loss and Take Profit levels unchanged
      else
        {
         //--- If the position failed to open, print the relevant message
         if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment))
            Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
        }
     }
  }

Ainda temos de adicionar o final, toque muito importante para as funções de processamento de evento:

Agora que todas as funções estão prontas, podemos otimizar os parâmetros. Tenha em mente que você precisa compilar o código do arquivo principal do programa.

 

Otimizando Parâmetros e Testando o Consultor Especialista

O Testador de Estratégia deve ser configurado como mostrado abaixo:

Fig. 1. Configurações do Testador de Estratégia.

Fig. 1. Configurações do Testador de Estratégia.

Além disso, definimos os parâmetros do Consultor Especialista para otimização (veja também o arquivo anexo *.set com configurações):

Fig. 2. Configurações do Consultor Especialista.

Fig. 2. Configurações do Consultor Especialista.

A otimização demorou aproximadamente 40 minutos em um processador dual-core. O gráfico de otimização permite que você avalie parcialmente a qualidade de um sistema de negócios, baseado nos resultados na zona de lucro:

Fig. 3. Gráfico de otimização.

Fig. 3. Gráfico de otimização.

Resultados do teste do fator máximo de recuperação são como se segue:

Fig. 4. Resultados do teste do fator máximo de recuperação.

Fig. 4. Resultados do teste do fator máximo de recuperação.

 

Conclusão

Anexo ao artigo está o arquivo com os códigos-fonte do Consultor Especialista. Uma vez que for extraído, você precisa colocar a pasta de arquivo \TestIndicatorConditions no \MQL5\Experts. Para garantir a correta operação do Consultor Especialista, o indicador MultiRange_PCH deve ser baixado e colocado em \MQL5\Indicators.