English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Guia passo a passo para iniciantes para escrever um Expert Advisor no MQL5

Guia passo a passo para iniciantes para escrever um Expert Advisor no MQL5

MetaTrader 5Sistemas de negociação | 24 dezembro 2013, 08:13
61 421 16
Samuel Olowoyo
Samuel Olowoyo

Introdução

Este artigo é destinado a iniciantes que desejam aprender como escrever Consultores Especialistas simples na nova linguagem MQL5. Nós começaremos primeiro definindo o que queremos que o nosso CE (Consultor Especialista) faça, e descrição então passaremos para como nós queremos que o CE o faça.


1. Estratégia de negociação

O que o nosso CE fará:

  • Ele irá monitorar um indicador particular e, quando uma certa condição for alcançada (ou certas condições foram alcançadas), ele irá colocar uma negociação (tanto uma posição curta/vendida ou longa/comprada), dependendo da condição presente que foi alcançada.

O mencionado acima é chamado de estratégia de negociação. Antes que você possa escrever um CE, você precisa primeiro desenvolver a estratégia que você deseja automatizar no CE. Então, neste caso, deixe-nos modificar a afirmação acima de forma que ela reflita a estratégia que nós queremos desenvolver no CE.

  • Nós utilizaremos um indicador chamado média móvel com um período de 8 (você pode escolher qualquer período, mas para os propósitos da nossa estratégia, nós usaremos 8).

  • Queremos que o nosso CE ofereça uma negociação longa (compra) quando a média móvel-8 (para o propósito neste assunto, eu irei me referir a ela como MM-8) estiver aumentando e o preço estiver acima dela, e oferecerá uma negociação curta (venda) quando a MM-8 estiver diminuindo e o preço estiver abaixo dela.
  • Também utilizaremos outro indicador chamado movimento direcional médio (ADX) com período 8 também para nos ajudar a determinar se o mercado está em tendência ou não. Estamos fazendo isso porque apenas queremos entrar na negociação quando o mercado estiver em tendência e relaxar quando o mercado estiver variando (isto é, sem tendência). Para alcançar isso, apenas ofereceremos a nossa negociação (comprar ou vender) quando as condições acima forem atendidas e o valor do ADX for maior que 22. Se o ADX for maior do que 22 porém diminuindo, ou o ADX for menor do que 22, não negociaremos, mesmo que a condição B tenha sido atendida.
  • Também queremos nos proteger definindo uma ordem para parar perda (ou stop loss) de 30 pontos, e para o nosso alvo de lucro; definiremos como objetivo um lucro de 100 pontos.
  • Também queremos que nosso CE procure por oportunidades de compra/venda somente quando uma nova barra tenha sido formada e também queremos nos certificar que abrimos uma posição de compra, se as condições de compra forem atendidas e já não tivermos uma em aberto, e abrir uma posição de venda quando as condições de venda forem atendidas e já não tivermos uma em aberto.

Agora desenvolvemos a nossa estratégia; é a hora de começar a escrever o nosso código.


2. Escrever um Expert Advisor

2.1 Assistente do MQL5

Inicie com a execução do editor de linguagem do MetaQuotes 5. Pressione Ctrl+N ou clique no botão Novo na barra do menu.

Figura 1. Iniciando um novo documento do MQL5

Figura 1. Iniciando um novo documento do MQL5

Na janela do instalador do MQL5, selecione consultor especialista e clique em "Próximo" como mostrado na figura 2:

Figura 2. Selecionando um tipo de documento

Figura 2. Selecionando um tipo de programa

Na próxima janela, digite o nome que você quer dar ao seu CE na caixa Nome. Neste caso, eu digitei My_First_EA. Você pode então digitar o seu nome na caixa Autor e também o endereço da sua página da internet ou endereço de e-mail na caixa Link (se você tiver um).

Figura 3. Propriedades gerais do consultor especialista

Figura 3. Propriedades gerais do consultor especialista

Já que queremos ser capazes de mudar alguns dos parâmetros para o nosso CE de forma a ver quais dos valores podem nos fornecer melhor resultado, nós iremos adicioná-los clicando no botão "Adicionar".

Figura 4. Configurando os parâmetros de entrada do CE

Figura 4. Configurando os parâmetros de entrada do CE

Em nosso CE, queremos ser capazes de experimentar com as nossas configurações de ordem do tipo parar perda, obter lucro, período ADX e período de média móvel, então iremos defini-los nesse momento.

Clique duas vezes sob a seção Nome e digite o nome do parâmetro, então clique duas vezes sobre o Tipo para selecionar o tipo de dados para o parâmetro e clique duas vezes sob a seção Valor inicial e digite o valor inicial para o parâmetro.

Quando terminar, ele deve se parecer com isto:

Figura 5. Tipos de dados dos parâmetros de entrada do CE

Figura 5. Tipos de dados dos parâmetros de entrada do CE

Como pode ser visto acima, foi selecionado o tipo de dado inteiro (int) para todos os parâmetros. Vamos falar um pouco sobre tipos de dados.

  • char: O tipo char toma 1 byte de memória (8 bits) e permite expressar em notação binária 2^8=256 valores. O tipo char pode conter ambos valores positivos e negativos. A mudança de valores é de -128 a 127.
  • uchar : O tipo inteiro uchar também ocupa 1 byte de memória, assim como o tipo de char, mas ao contrário dele o uchar destina-se apenas a valores positivos. O valor mínimo é zero, o valor máximo é 255. A primeira letra u no nome do tipo uchar é a abreviação para não assinado.
  • short: O tamanho do tipo short é de 2 bytes (16 bits) e, consequentemente, ele segue expressando a faixa de valores igual a 2 para a potência 16: 2^16 = 65 536. Uma vez que o tipo short é um tipo assinado, ele contém ambos valores positivos e negativos, a faixa de valores está entre -32 768 e 32 767.
  • ushort: O tipo não assinado short é o tipo ushort, que também possui um tamanho de 2 bytes. O valor mínimo é 0, e o valor máximo é 65 535.
  • int : O tamanho do tipo int é de 4 bytes (32 bits). O valor mínimo é -2 147 483 648, o valor máximo é 2 147 483 647.
  • uint : O tipo inteiro não assinado é o uint. Ele toma 4 bytes de memória e permite expressar inteiros de 0 a 4 294 967 295.
  • long : O tamanho do tipo long é de 8 bytes (64 bits). O valor mínimo é -9 223 372 036 854 775 808, o valor máximo é 9 223 372 036 854 775 807.
  • ulong : O tipo ulong também ocupa 8 bytes e pode armazenar valores de 0 a 18 446 744 073 709 551 615.

Pela descrição acima dos vários tipos de dados, os tipos inteiros não assinados não são projetados para armazenar valores negativos, qualquer tentativa de configurar um valor negativo pode levar a consequências inesperadas. Por exemplo, se você quer armazenar valores negativos, você não pode armazená-los dentro dos tipos não assinados (isto é, uchar, uint, ushort, ulong).

Voltando ao ao nosso CE. Olhando os tipos de dados, você irá concordar que devemos utilizar os tipos de dados char ou uchar já que os dados que pretendemos armazenar nestes parâmetros sejam menores do que 127 ou 255 respectivamente. Para bom gerenciamento da memória, essa é a melhor coisa a se fazer. Entretanto, pelo propósito desta discussão, iremos nos ater ao tipo int.

Uma vez que tenha acabado de configurar todos os parâmetros necessários, clique no botão Finished e o editor do MetaQuotes criará um esqueleto do código para você como mostrado na próxima figura.


Vamos separar o código em várias seções para melhor entendimento.

A parte superior (cabeçalho) do código é onde a propriedade do CE é definida. Você pode ver que aqui estão os valores que você preencheu durante o assistente do MQL5 na figura 3.

Nesta seção do código, você pode definir parâmetros adicionais como descrição (breve descrição do texto do CE), declarar constantes, incluir arquivos adicionais ou funções mais importantes.


Quando uma declaração começa com um símbolo #, ela é chamada de diretiva de pré-processamento e não termina com um ponto e vírgula ‘;’ outros exemplos de diretivas de pré-processamento incluem:

#define :

A diretiva #define é utilizada para uma declaração de constantes. É escrita na forma

#define identificador token_string

O que isso faz é substituir cada ocorrência de identificador no seu código com o valor token_string.

Exemplo:

#define ABC 100
#define COMPANY_NAME "MetaQuotes Software Corp."

Ele substituirá todas as ocorrências de COMPANY_NAME pela sequência "MetaQuotes Software Corp." ou ele substituirá todas as ocorrências de ABC pelo char (ou inteiro) 100 em seu código.

Você pode ler mais sobre as diretivas de pré-processamento no manual do MQL5. Vamos agora continuar com a nossa discussão.

A segunda parte do cabeçalho do nosso código é a seção parâmetros de entrada:


Nós especificamos todos os parâmetros, que serão utilizados em nosso CE nesta seção. Estes incluem todas as variáveis que serão utilizadas por todas as funções que nós escreveremos em nosso CE.

Variáveis declaradas nesse nível são chamadas de Variáveis globais porque elas são acessíveis por todas as funções em nosso CE que possam precisar delas. Os parâmetros de entrada são parâmetros que podem apenas ser modificados fora do nosso CE. Também podemos declarar outras variáveis que iremos manipular no curso do nosso CE mas não estarão disponíveis fora do nosso CE nesta seção.

A seguir está a função de inicialização do CE. Essa é a primeira função que é chamada quando o CE é iniciado ou anexado a um gráfico e é chamado apenas uma vez.


Essa seção é o melhor local para realizar algumas verificações importantes de forma a se certificar de que o nosso CE funciona muito bem.

Podemos decidir se o gráfico possui barras suficientes para o nosso CE funcionar, etc.

Também é o melhor local para pegar as cotações que utilizaremos para os nossos indicadores (indicadores ADX e de média móvel).


A função OnDeinit é chamada quando o CE é removido do gráfico.

Para o nosso CE, nós iremos liberar as cotações criadas para os nossos indicadores durante a inicialização nesta seção.


Essa função processa o evento NewTick, que é gerado quando uma nova cotação é recebida para um símbolo.

Observe que o consultor especialista não pode realizar operações de negócios se o uso dos consultores especialistas no terminal do cliente não estiver permitido (botão "Auto Negociação").

Figura 6. Auto Negociação está ativada

Figura 6. Auto Negociação está ativada

A maior parte dos nossos códigos que irão implementar a nossa estratégia de de negócios, desenvolvidos anteriormente, serão escritos nessa seção.

Agora que já observamos as várias seções do código para o nosso CE, vamos começar a adicionar mais conteúdo a este esqueleto.

2.2 SEÇÃO DE PARÂMETROS DE ENTRADA

//--- input parameters
input int      StopLoss=30;      // Stop Loss
input int      TakeProfit=100;   // Take Profit
input int      ADX_Period=8;     // ADX Period
input int      MA_Period=8;      // Moving Average Period
input int      EA_Magic=12345;   // EA Magic Number
input double   Adx_Min=22.0;     // Minimum ADX Value
input double   Lot=0.1;          // Lots to Trade
//--- Other parameters
int adxHandle; // handle for our ADX indicator
int maHandle;  // handle for our Moving Average indicator
double plsDI[],minDI[],adxVal[]; // Dynamic arrays to hold the values of +DI, -DI and ADX values for each bars
double maVal[]; // Dynamic array to hold the values of Moving Average for each bars
double p_close; // Variable to store the close value of a bar
int STP, TKP;   // To be used for Stop Loss & Take Profit values

Como você pode ver, nós adicionamos mais parâmetros. Antes de continuarmos a discutir os novos parâmetros, vamos discutir algo que você pode ver agora. As duas barras normais ‘//’ nos permitem inserir comentários em nossos códigos. Com comentários, somos capazes de saber o que as nossas variáveis significam, ou o que nós estamos fazendo naquele momento de tempo em nosso código. Também fornece um melhor entendimento do nosso código. Há duas formas básicas de escrever comentários:

// Outros parâmetros ...

Esse é um comentário de uma linha:

/*

Esse é um comentário de várias linhas

*/

Esse é um comentário de várias linhas. Comentários com várias linhas começam com o par de símbolos /* e terminam com o par */.

O compilador ignora todos os comentários quando compilando o código.

Utilizar comentários de uma linha para os parâmetros de entrada é uma boa forma de fazer os usuários do CE entenderem o que esses parâmetros significam. Nas propriedades de entrada do CE, nossos usuários não irão visualizar o parâmetro em si, ao invés disso verão os comentários como mostrados abaixo:

Figura 7. Parâmetros de entrada do consultor especialista

Figura 7. Parâmetros de entrada do consultor especialista

Agora de volta para o nosso código...

Nós decidimos adicionar parâmetros adicionais ao nosso CE. O EA_Magic é o número mágico para todas as ordens pelo nosso CE. O valor ADX mínimo (Adx_Min) é declarado como um tipo de dado double. Um double é utilizado para armazenar constantes de ponto flutuante, que contém uma parte inteira, um ponto decimal, e uma parte de fração.

Exemplo:

double mysum = 123.5678;

double b7 = 0.09876;

O lote para negociação (Lote) representa o volume do instrumento financeiro que nós queremos negociar. Então nós declaramos outros parâmetros que utilizaremos:

O adxHandle deve ser utilizado para armazenar a cotação do indicador ADX, enquanto o maHandle irá armazenar a cotação para o indicador de média móvel. Os plsDI[], minDI[], adxVal[] são arranjos dinâmicos que irão manter os valores de +DI, -DI e ADX principal (do indicador do ADX) para cada barra do gráfico. O maVal[] é um arranjo dinâmico que irá manter os valores do indicador da média móvel para cada barra do gráfico.

A propósito, o que são arranjos dinâmicos? Um arranjo dinâmico é um arranjo declarado sem uma dimensão. Em outras palavras, nenhum valor é especificado no par de colchetes. Um arranjo estático, por outro lado, possui suas dimensões definidas no ponto da declaração.

Exemplo:

double allbars[20]; // this will take 20 elements

p_close é uma variável que nós utilizaremos para armazenar o preço de fechamento para a barra que nós iremos monitorar para verificar as nossas negociações de compra/venda.

STP e TKP serão utilizados para armazenar os valores de stop loss e take profit em nosso CE.

2.3. Seção de inicialização do CE

int OnInit()
  {
//--- Get handle for ADX indicator
   adxHandle=iADX(NULL,0,ADX_Period);
//--- Get the handle for Moving Average indicator
   maHandle=iMA(_Symbol,_Period,MA_Period,0,MODE_EMA,PRICE_CLOSE);
//--- What if handle returns Invalid Handle
   if(adxHandle<0 || maHandle<0)
     {
      Alert("Error Creating Handles for indicators - error: ",GetLastError(),"!!");
     }

Aqui nós obtemos as cotações do nosso indicador utilizando as funções do indicador respectivo.

A cotação do indicador ADX é obtida utilizando a função iADX. Ele toma o símbolo do gráfico (NULL também significa o símbolo atual no gráfico atual), o período do gráfico/cronograma (0 também significa o cronograma atual no gráfico atual), o período de média do ADX para calcular o índice (que nós definimos antes sob a seção de parâmetros de entrada) como parâmetros ou argumentos.

int iADX(
string symbol, // symbol name
ENUM_TIMEFRAMES period, // period
int adx_period // averaging period
);

A cotação do indicador da média móvel é obtida utilizando a função iMA. Ela possui os seguintes argumentos:

  • o símbolo do gráfico (que pode ser obtido utilizando _symbol, symbol() ou NULL para o símbolo atual no gráfico atual),
  • o gráfico do período/cronograma (que pode ser obtido utilizando _period, period(), ou 0 para o cronograma atual no gráfico atual),
  • O período médio da média móvel (que nós definimos antes sob a seção parâmetros de entrada),
  • a mudança do indicador relativa ao gráfico de preços (a mudança aqui é 0),
  • o tipo de suavização da média móvel (poderia ser qualquer um dos seguintes métodos de cálculo da média: Média Simples-MODE_SMA, Média exponencial-MODE_EMA, Média suavizada-MODE_SMMA ou Média ponderada linear-MODE_LWMA), e
  • o preço utilizado para o cálculo da média (aqui utilizamos o preço de fechamento).

int iMA(
string symbol, // symbol name
ENUM_TIMEFRAMES period, // period
int ma_period, // averaging period
int ma_shift, // horizontal shift
ENUM_MA_METHOD ma_method, // smoothing type
ENUM_APPLIED_PRICE applied_price // type of price or handle
);

Por favor, leia o manual do MQL5 para obter mais detalhes sobre essas funções do indicador. Ele lhe dará um melhor entendimento de como utilizar cada indicador.

Nós novamente tentamos verificar a presença de qualquer erro caso a função não tenha devolvido a cotação com sucesso, nós receberemos um erro INVALID_HANDLE. Usamos a função de alerta para exibir o erro utilizando a função GetlastError.

//--- Let us handle currency pairs with 5 or 3 digit prices instead of 4
   STP = StopLoss;
   TKP = TakeProfit;
   if(_Digits==5 || _Digits==3)
     {
      STP = STP*10;
      TKP = TKP*10;
     }

Decidimos armazenar os valores de parar perdas e obter lucros nas variáveis STP e TKP que declaramos anteriormente. Por que estamos fazendo isso?

É porque os valores armazenados nos parâmetros de ENTRADA são somente para leitura, eles não podem ser modificados. Então aqui nós queremos nos certificar de que o nosso CE funciona bem com todos os corretores. Digits ou Digits() retorna o número de dígitos decimais determinando a precisão do preço do símbolo do gráfico atual. Para um gráfico de preços com 5 dígitos ou 3 dígitos, nós multiplicamos ambos o parar perdas e o obter lucros por 10.

2.4. SEÇÃO DE DESINICIALIZAÇÃO DO CE

Uma vez que essa função é utilizada sempre que o CE é desativado ou removido de um gráfico, nós iremos liberar todas as cotações de indicadores que foram criadas durante o processo de inicialização aqui. Nós criamos duas cotações, uma para o indicador do ADX e outra cotação para o indicador da média móvel.

Nós utilizaremos a função IndicatorRelease() para conseguir isso. Leva apenas um argumento (a cotação do indicador).

bool IndicatorRelease(
int indicator_handle, // indicator handle
);

A função remove uma cotação do indicador e libera o bloco de cálculo do indicador, se ele não foi utilizado.

2.5 A SEÇÃO ONTICK DO CE

A primeira coisa que precisamos fazer é verificar se temos barras suficientes no gráfico atual. Podemos obter o total de barras no histórico de qualquer gráfico utilizando a função Bars. São necessários dois parâmetros, o símbolo (pode ser obtido utilizando _Symbol ou Symbol(). Esses dois retornam o símbolo atual para o gráfico atual no qual o nosso CE está anexado) e o período ou cronograma do gráfico presente (pode ser obtido utilizando Period ou Period(). Esses dois irão retornar o cronograma do gráfico atual no qual o CE está anexado).

Se o total disponível de barras é menos do que 60, nós queremos que o nosso CE relaxe até que nós tenhamos barras o suficiente disponíveis no gráfico. A função Alert exibe uma mensagem em uma janela separada. Ela toma quaisquer valores separados por vírgulas como parâmetros/argumentos. Neste caso, nós possuímos apena um valor de cadeia (string). O retorno sai da inicialização do nosso CE.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
// Do we have enough bars to work with
   if(Bars(_Symbol,_Period)<60) // if total bars is less than 60 bars
     {
      Alert("We have less than 60 bars, EA will now exit!!");
      return;
     }
// We will use the static Old_Time variable to serve the bar time.
// At each OnTick execution we will check the current bar time with the saved one.
// If the bar time isn't equal to the saved time, it indicates that we have a new tick.
   static datetime Old_Time;
   datetime New_Time[1];
   bool IsNewBar=false;

// copying the last bar time to the element New_Time[0]
   int copied=CopyTime(_Symbol,_Period,0,1,New_Time);
   if(copied>0) // ok, the data has been copied successfully
     {
      if(Old_Time!=New_Time[0]) // if old time isn't equal to new bar time
        {
         IsNewBar=true;   // if it isn't a first call, the new bar has appeared
         if(MQL5InfoInteger(MQL5_DEBUGGING)) Print("We have new bar here ",New_Time[0]," old time was ",Old_Time);
         Old_Time=New_Time[0];            // saving bar time
        }
     }
   else
     {
      Alert("Error in copying historical times data, error =",GetLastError());
      ResetLastError();
      return;
     }

//--- EA should only check for new trade if we have a new bar
   if(IsNewBar==false)
     {
      return;
     }
 
//--- Do we have enough bars to work with
   int Mybars=Bars(_Symbol,_Period);
   if(Mybars<60) // if total bars is less than 60 bars
     {
      Alert("We have less than 60 bars, EA will now exit!!");
      return;
     }

//--- Define some MQL5 Structures we will use for our trade
   MqlTick latest_price;     // To be used for getting recent/latest price quotes
   MqlTradeRequest mrequest;  // To be used for sending our trade requests
   MqlTradeResult mresult;    // To be used to get our trade results
   MqlRates mrate[];         // To be used to store the prices, volumes and spread of each bar
   ZeroMemory(mrequest);     // Initialization of mrequest structure

O consultor especialista realizará operações de negociação no início de uma nova barra, então é necessário resolver o problema com a nova identificação da barra. Em outras palavras, nós queremos nos certificar de que o nosso CE não verifique as configurações longas/curtas a cada variação, nós apenas queremos que o nosso CE verifique as posições longas/curtas quando houver uma nova barra.

Começamos declarando uma variável datetime estática Old_Time, que irá armazenar o tempo da barra. Declaramos ela como estática porque nós queremos que o valor seja retido na memória até a próxima utilização da função OnTick. Então seremos capazes de comparar o seu valor com a variável New_Time (também do tipo de dados datetime), que é um arranjo de um elemento para manter o novo (atual) tempo da barra. Também declaramos uma variável de tipo de dados bool IsNewBar e configuramos seu valor para false. Isto é porque queremos que esse valor seja VERDADEIRO apenas quando nós possuirmos uma nova barra.

Utilizamos a função CopyTime para conseguir o tempo da barra atual. Ela copia o tempo da barra para o arranjo New_Time com um elemento, se ele for bem sucedido, nós comparamos o tempo de uma nova barra com o tempo da barra anterior. Se os tempos não forem iguais, isso significa que temos uma nova barra, e configuramos a variável IsNewBar para VERDADEIRO e salvamos o valor do tempo da barra atual para a variável Old_Time.

A variável IsNewBar indica que temos uma barra nova. Se for falso, terminamos a execução da função OnTick.

Dê uma olhada no código:

if(MQL5InfoInteger(MQL5_DEBUGGING)) Print("We have new bar here ",New_Time[0]," old time was ",Old_Time);

Ele verifica a execução do modo de depuração, ele irá imprimir uma mensagem sobre os tempos da barra quando em modo de depuração, nós iremos considerá-lo mais a frente.

A próxima coisa que queremos fazer aqui é verificar se nós temos barras suficientes para trabalhar. Por que repetir isso? Só queremos nos certificar de que o nosso CE funciona corretamente. Deve-se notar que enquanto a função OnInit é chamada apenas uma vez quando o CE está anexado a um gráfico, a função OnTick é chamada toda vez que há um novo ponto (cotação de preço).

Você pode notar que fizemos de forma diferente novamente aqui. Decidimos armazenar as barras totais no histórico que obtivemos da expressão:

int Mybars=Bars(_Symbol,_Period);

em uma nova variável, Mybars, declarada dentro da função OnTick. Esse tipo de variável é uma variável local, ao contrário da variável que nós declaramos na seção de PARÂMETROS DE ENTRADA do nosso código. Enquanto as variáveis, declaradas na seção de parâmetros de entrada do nosso código, estão disponíveis para todas as funções dentro do nosso código que possam precisar delas, variáveis declaradas dentro de uma única função são limitadas e disponíveis para aquela função somente. Ela não pode ser utilizada fora daquela função.

A seguir, nós declaramos algumas variáveis do tipo de estrutura do MQL5 que serão utilizadas nessa seção do nosso CE. O MQL5 possui um número grande de estruturas integradas que torna as coisas bem fáceis para os desenvolvedores do CE. Vamos fazer as estruturas uma após a outra.

MqlTick

Essa é uma estrutura utilizada para armazenar os preços mais recentes de símbolos.

struct MqlTick
{
datetime time; // Hora da última atualização dos preços
double bid; // preço de compra (Bid) atual
double ask; // Preço de venda (Ask) atual
double last; // Preço da última negociação (Last)
ulong volume; // Volume para o último preço atual
};

Qualquer variável declarada como sendo do tipo MqlTick pode ser facilmente utilizada para obter os valores atuais de Venda (Ask), Compra (Bid), Última (Last) eVolume uma vez que você chama a função SymbolInfoTick().

Então nós declaramos latest_price como do tipo MqlTick de forma que pudéssemos utilizá-la para conseguir os preços de venda (Ask) e compra (Bid).

MqlTradeRequest

Esta estrutura é utilizada para realizar todos os pedidos de negociação para uma operação de negociação. Ele contém, em sua estrutura, todos os campos necessários para realizar um acordo de negociação.

struct MqlTradeRequest
{
ENUM_TRADE_REQUEST_ACTIONS action; // Trade operation type
ulong magic; // Expert Advisor ID (magic number)
ulong order; // Order ticket
string symbol; // Trade symbol
double volume; // Requested volume for a deal in lots
double price; // Price
double stoplimit; // StopLimit level of the order
double sl; // Stop Loss level of the order
double tp; // Take Profit level of the order
ulong deviation; // Maximal possible deviation from the requested price
ENUM_ORDER_TYPE type; // Order type
ENUM_ORDER_TYPE_FILLING type_filling; // Order execution type
ENUM_ORDER_TYPE_TIME type_time; // Order execution time
datetime expiration; // Order expiration time (for the orders of ORDER_TIME_SPECIFIED type)
string comment; // Order comment
};

Qualquer variável declarada como do tipo MqlTradeRequest pode ser utilizada para enviar pedidos para nossas operações de negociação. Aqui nós declaramos o mrequest como do tipo MqlTradeRequest.

MqlTradeResult

O resultado de qualquer operação de negociação é retornado como uma estrutura especial pré-definida do tipo MqlTradeResult. Qualquer variável declarada como do tipo MqlTradeResult será capaz de acessar os resultados do pedido de negociação.

struct MqlTradeResult
{
uint retcode; // Operation return code
ulong deal; // Deal ticket, if it is performed
ulong order; // Order ticket, if it is placed
double volume; // Deal volume, confirmed by broker
double price; // Deal price, confirmed by broker
double bid; // Current Bid price
double ask; // Current Ask price
string comment; // Broker comment to operation (by default it is filled by the operation description)
};

Aqui nós declaramos mresult como sendo do tipo MqlTradeResult.

MqlRates

O preço (aberto, fechado, alto, baixo), a hora, os volumes de cada barra e a distribuição de um símbolo são armazenados nessa estrutura. Qualquer arranjo declarado como sendo do tipo MqlRates pode ser utilizado para armazenar preço, volumes e histórico de diferenças (spread) de um símbolo.

struct MqlRates
{
datetime time; // Period start time
double open; // Open price
double high; // The highest price of the period
double low; // The lowest price of the period
double close; // Close price
long tick_volume; // Tick volume
int spread; // Spread
long real_volume; // Trade volume
};

Aqui nós declaramos um arranjo mrate[] que irá ser utilizado para armazenar essas informações.

/*
     Let's make sure our arrays values for the Rates, ADX Values and MA values 
     is store serially similar to the timeseries array
*/
// the rates arrays
   ArraySetAsSeries(mrate,true);
// the ADX DI+values array
   ArraySetAsSeries(plsDI,true);
// the ADX DI-values array
   ArraySetAsSeries(minDI,true);
// the ADX values arrays
   ArraySetAsSeries(adxVal,true);
// the MA-8 values arrays
   ArraySetAsSeries(maVal,true);

Em seguida nós decidimos configurar todos os arranjos que nós utilizaremos para armazenar os detalhes das barras como série. Isso é para nos certificar de que os valores que serão copiados para os arranjos serão indexados como a timeseries, isto é, 0, 1, 2, 3, (para corresponder com o índice das barras. Então nós utilizamos a função ArraySetAsSeries().

bool ArraySetAsSeries(
void array[], // array by reference
bool set // true denotes reverse order of indexing
);

Deve-se notar que isso também pode ser feito uma vez na seção de inicialização do nosso código. No entanto, eu decidi mostrar isso nesse momento para o bem da nossa explicação.

//--- Get the last price quote using the MQL5 MqlTick Structure
   if(!SymbolInfoTick(_Symbol,latest_price))
     {
      Alert("Error getting the latest price quote - error:",GetLastError(),"!!");
      return;
     }

Agora nós utilizamos a função SymbolInfoTick para obter a cotação de preço mais recente. Essa estrutura toma dois argumentos – símbolo do gráfico e a variável de estrutura MqlTick (latest_price). Novamente, se houver erros, nós o reportamos.

//--- Get the details of the latest 3 bars
   if(CopyRates(_Symbol,_Period,0,3,mrate)<0)
     {
      Alert("Error copying rates/history data - error:",GetLastError(),"!!");
      return;
     }

Em seguida, nós copiamos a informação sobre as três últimas barras em noso arranjo do tipo Mqlrates utilizando a função CopyRates. A função CopyRates é utilizada para conseguir dados de histórico da estrutura do MqlRates de um período/símbolo em quantidade específica dentro do arranjo do tipo MqlRates.

int CopyRates(
string symbol_name, // symbol name
ENUM_TIMEFRAMES timeframe, // period
int start_pos, // start position
int count, // data count to copy
MqlRates rates_array[] // target array to copy
);

O nome do símbolo é obtido utilizando ‘_symbol’, o período/cronograma atual é obtido utilizando ‘_period’. Para a posição de início, nós começaremos da barra atual, barra 0 e nós contaremos apenas três barras, barras 0, 1, e 2. O resultado será armazenado em nosso arranjo, mrate[].

O arranjo mrate[] agora contém todas as informações de preço, tempo, volumes e distribuição para as barras 0, 1 e 2. Dessa forma para conseguir detalhes de qualquer barra, nós utilizaremos o seguinte:

mrate[bar_number].bar_property

Por exemplo, nós podemos ter a seguinte informação em cada barra.

mrate[1].time // Bar 1 Start time
mrate[1].open // Bar 1 Open price
mrate[0].high // Bar 0 (current bar) high price, etc

Em seguida nós copiamos todos os valores indicadores nos arranjos dinâmicos que nós declaramos utilizando a função CopyBuffer.

int CopyBuffer(
int indicator_handle, // indicator handle
int buffer_num, // indicator buffer number
int start_pos, // start position
int count, // amount to copy
double buffer[] // target array to copy
);

A cotação do indicador é a cotação que nós criamos na seção OnInit. Quanto a números de amortecimento, o indicador ADX possui três amortecimentos:

  • 0 - MAIN_LINE,
  • 1 - PLUSDI_LINE,
  • 2 - MINUSDI_LINE.

O indicador da média móvel possui apenas um (1) amortecimento:

  • 0 – MAIN_LINE.

Nós copiamos da barra atual (0) nas duas últimas barras. Então a quantidade de registros para copiar é 3 (barras 0, 1, e 2). O buffer[] são os arranjos dinâmicos alvo que nós declaramos anteriormente – adxVal, plsDI, minDI e maVal.

Como você pode ver aqui novamente, nós tentamos capturar qualquer erro que possa ocorrer em nosso processo de cópia. Se houver um erro, não há necessidade de prosseguir.

É importante observar que as funções CopyBuffer() e CopyRates() retornam ao número total de registros copiados com sucesso enquanto ela retorna -1 em caso de um erro. É por isso que nós verificamos valores menores que 0 (zero) nas funções verificadores de erro aqui.

//--- Copy the new values of our indicators to buffers (arrays) using the handle
   if(CopyBuffer(adxHandle,0,0,3,adxVal)<0 || CopyBuffer(adxHandle,1,0,3,plsDI)<0
      || CopyBuffer(adxHandle,2,0,3,minDI)<0)
     {
      Alert("Error copying ADX indicator Buffers - error:",GetLastError(),"!!");
      return;
     }
   if(CopyBuffer(maHandle,0,0,3,maVal)<0)
     {
      Alert("Error copying Moving Average indicator buffer - error:",GetLastError());
      return;
     }

Nesse momento, queremos verificar se nós já possuímos uma posição de compra ou venda aberta, em outras palavras, nós queremos nos certificar de que nós possuímos UMA negociação de compra ou venda aberta de cada vez. Nós não queremos abrir uma nova compra se nós já possuímos uma, e nós não queremos abrir uma nova venda se nós já abrimos uma.

Para alcançar isso primeiramente declaramos dois tipos de dados bool variáveis (Buy_opened e Sell_opened) que manterão um valor REAL se já possuirmos uma posição aberta tanto para compra ou venda.

//--- we have no errors, so continue
//--- Do we have positions opened already?
    bool Buy_opened=false;  // variable to hold the result of Buy opened position
    bool Sell_opened=false; // variable to hold the result of Sell opened position
    
    if (PositionSelect(_Symbol) ==true)  // we have an opened position
    {
         if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
         {
            Buy_opened = true;  //It is a Buy
         }
         else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
         {
            Sell_opened = true; // It is a Sell
         }
    }

Nós usamos a função de negociação PositionSelect para saber se possuímos uma posição aberta. Essa função retorna VERDADEIRA se já possuímos uma posição aberta e FALSA se não possuímos nenhuma.

bool  PositionSelect(
   string  symbol      // Symbol name 
 );

Ela toma, como o maior argumento/parâmetro, o símbolo (par de moedas) que nós queremos verificar. Aqui nós utilizamos _symbol porque estamos verificando o símbolo atual (par de moedas).

Se a expressão voltar VERDADEIRA então queremos verificar se a posição aberta é uma compra ou uma venda. Nós utilizamos a função PositionGetInteger para isso. Ela nos dá o tipo de posição aberta quando nós utilizamos ela com o modificadorPOSITION_TYPE. Ela retorna o Position type identifier que pode tanto serPOSITION_TYPE_BUY ou POSITION_TYPE_SELL

long  PositionGetInteger(
   ENUM_POSITION_PROPERTY  property_id      // Property identifier
   );

Em nosso caso, a utilizamos para determinar qual posição já abrimos. Se for uma venda, armazenamos um valor VERDADEIRO em Sell_opened e se for uma compra, armazenamos um valor VERDADEIRO em Buy_opened. Seremos capazes de utilizar essas duas variáveis posteriormente quando verificarmos as condições de compra e venda posteriormente em nosso código.

Agora é hora de armazenar o preço de fechamento para a barra que utilizaremos para a nossa configuração de compra/venda. Lembre-se que declaramos uma variável para isso anteriormente

// Copy the bar close price for the previous bar prior to the current bar, that is Bar 1

   p_close=mrate[1].close;  // bar 1 close price

Tendo feito isso, iremos agora proceder para o próximo passo.

/*
    1. Check for a long/Buy Setup : MA-8 increasing upwards, 
    previous price close above it, ADX > 22, +DI > -DI
*/
//--- Declare bool type variables to hold our Buy Conditions
   bool Buy_Condition_1 = (maVal[0]>maVal[1]) && (maVal[1]>maVal[2]); // MA-8 Increasing upwards
   bool Buy_Condition_2 = (p_close > maVal[1]);         // previuos price closed above MA-8
   bool Buy_Condition_3 = (adxVal[0]>Adx_Min);          // Current ADX value greater than minimum value (22)
   bool Buy_Condition_4 = (plsDI[0]>minDI[0]);          // +DI greater than -DI

//--- Putting all together   
   if(Buy_Condition_1 && Buy_Condition_2)
     {
      if(Buy_Condition_3 && Buy_Condition_4)
        {
         // any opened Buy position?
         if (Buy_opened) 
         {
            Alert("We already have a Buy Position!!!"); 
            return;    // Don't open a new Buy Position
         }
         mrequest.action = TRADE_ACTION_DEAL;                                // immediate order execution
         mrequest.price = NormalizeDouble(latest_price.ask,_Digits);          // latest ask price
         mrequest.sl = NormalizeDouble(latest_price.ask - STP*_Point,_Digits); // Stop Loss
         mrequest.tp = NormalizeDouble(latest_price.ask + TKP*_Point,_Digits); // Take Profit
         mrequest.symbol = _Symbol;                                         // currency pair
         mrequest.volume = Lot;                                            // number of lots to trade
         mrequest.magic = EA_Magic;                                        // Order Magic Number
         mrequest.type = ORDER_TYPE_BUY;                                     // Buy Order
         mrequest.type_filling = ORDER_FILLING_FOK;                          // Order execution type
         mrequest.deviation=100;                                            // Deviation from current price
         //--- send order
         OrderSend(mrequest,mresult);

Agora é hora de iniciar a verificação para uma oportunidade de compra.

Vamos organizar a expressão acima conforme ela representa a estratégia que nós projetamos anteriormente. Nós estamos declarando uma variável do tipo bool para cada uma de nossas condições que devem ser atendidas antes que uma ordem possa ser feito. Uma variável do tipo boll pode apenas conter VERDADEIRO ou FALSO. Então, a nossa estratégia de compra foi dividida em quatro condições. Se qualquer uma das condições for atendida ou satisfeita, então um valor de VERDADEIRO é armazenado em nossa variável do tipo bool, de modo contrário, um valor de FALSO será armazenado. Vamos olhar pra elas mais uma vez.

bool Buy_Condition_1 = (maVal[0]>maVal[1]) && (maVal[1]>maVal[2]);

Aqui nós estamos olhando para os valores MA-8 nas barras 0, 1 e 2. Se o valor de MA-8 na barra atual for maior do que seu valor na barra Barra 1 anterior e também o valor de MA-8 na barra 1 for maior do que sei valor na barra 2, significa que o MA-8 está crescendo para cima. Isso satisfaz uma das nossas condições para uma configuração de compra.

bool Buy_Condition_2 = (p_close > maVal[1]); 

Essa expressão está verificando para ver se o preço de fechamento da barra 1 é maior do que o valor de MA-8 no mesmo período (período da barra). Se o preço for maior, então a nossa segunda condição também foi satisfeita, e nós podemos verificar as outras condições. No entanto, se as duas condições que acabamos de considerar não foram atendidas, então não há necessidade de verificar outras condições. É por isso que decidimos incluir as próximas expressões dentro dessas duas condições iniciais (expressões).

bool Buy_Condition_3 = (adxVal[0]>Adx_Min);

Agora queremos verificar se o valor atual de ADX (valor de ADX na barra 0) é maior do que o valor de ADX máximo declarado nos parâmetros de entrada. Se essa expressão for verdadeira, isto é, o valor atual de ADX é maior do que o valor mínimo necessário; nós também queremos nos certificar de que o valor plusDI é maior do que o valor minusDI. Isso é o que alcançamos na próxima expressão.

bool Buy_Condition_4 = (plsDI[0]>minDI[0]);

Se todas essas condições forem atendidas, isto é, se eles retornam verdadeiras, então nós queremos nos certificar de que nós não vamos abrir uma nova posição de compra se nós já possuímos uma. É hora de verificar o valor da variável Buy_opened que declaramos anteriormente em nosso código.

// any opened Buy position?
if (Buy_opened) 
   {
      Alert("We already have a Buy Position!!!"); 
      return;    // Don't open a new Buy Position
   }

Se Buy_opened for verdadeira, nós não queremos abrir outra posição de compra, então, exibimos um alerta para nos informar e aí retornar de forma que o nosso CE irá agora esperar pela próxima variação. No entanto, se Buy_opened é FALSO, então preparamos os nossos registros utilizando a variável do tipo MqlTradeRequest (mrequest) que declaramos anteriormente para enviar a nossa ordem.

  • A ação aqui, que é o tipo de operação de negociação, é TRADE_ACTION_DEAL porque nós estamos fazendo uma ordem de negociação para uma execução imediata. Se estamos modificando uma ordem, então utilizaremosTRADE_ACTION_MODIFY. Para deletar uma ordem vamos utilizar TRADE_ACTION_REMOVE. Nós utilizamos o nosso tipo MqlTick latest_price para conseguir o preço preço de venda mais atual. O preço da ordem stop loss é obtido ao subtrair nosso StopLoss em pontos do Preço de Venda enquanto o preço da ordem obter lucros é obtido ao adicionar nosso TakeProfit em pontos ao Preço de Venda. Você também observará que utilizamos a função NormalizeDouble para os valores de preço de venda, valores de parar perdas e obter lucros, é uma boa prática sempre normalizar esses preços para o número de dígitos do par de moedas de câmbio antes de enviá-los para o servidor de comercialização.
  • O symbol é o atual símbolo (_Symbol ou Symbol()). O tipo de ordem é o tipo de ordem que nós estamos colocando, aqui nós estamos colocando uma ordem de compra ORDER_TYPE_BUY. Para uma ordem de venda, será ORDER_TYPE_SELL.
  • A ordem type_filling é o tipo de execução da ordem; ORDER_FILLING_FOK significa que o acordo pode ser executado exclusivamente com um volume específico no preço igual ou melhor do que o preço especificado pela ordem. Se não houver volume de ordens suficiente no símbolo ordem, a ordem não será executada.

A função OrderSend() toma dois argumentos, a variável tipo MqlTradeRequest e a variável tipo MqlTradeResult.

bool  OrderSend(
   MqlTradeRequest&  request      // query structure
   MqlTradeResult&   result       // structure of the answer
   );

Como você pode ver, nós utilizamos o nosso tipo variável MqlTradeRequest e a variável tipo MqlTradeResult ao colocar a nossa ordem utilizando OrderSend.

         // get the result code
         if(mresult.retcode==10009 || mresult.retcode==10008) //Request is completed or order placed
           {
            Alert("A Buy order has been successfully placed with Ticket#:",mresult.order,"!!");
           }
         else
           {
            Alert("The Buy order request could not be completed -error:",GetLastError());
            ResetLastError();           
            return;
           }

Tendo enviado a nossa ordem, utilizaremos a variável tipo MqlTradeResult para verificar o resultado da nossa ordem. Se a nossa ordem é executada com sucesso, queremos ser informados, se não, nós queremos saber também. Com o tipo MqlTradeResult variável ‘mresult’ podemos acessar o código de retorno da operação e também o número do ticket da ordem se a ordem foi colocada.

O código de retorno 10009 mostra que a ordem OrderSend foi completada com sucesso, enquanto que 10008 mostra que a nossa ordem foi colocada. É por isso que verificamos para qualquer um desses dois códigos de retorno. Se possuímos qualquer um deles, temos certeza de que a nossa ordem foi completa ou foi feita.

Para verificar se há uma oportunidade de venda, verificamos o oposto do que fizemos para oportunidade de compra exceto o nosso ADX que deve ser maior do que o valor minimo especificado.

/*
    2. Check for a Short/Sell Setup : MA-8 decreasing downwards, 
    previous price close below it, ADX > 22, -DI > +DI
*/
//--- Declare bool type variables to hold our Sell Conditions
   bool Sell_Condition_1 = (maVal[0]<maVal[1]) && (maVal[1]<maVal[2]);  // MA-8 decreasing downwards
   bool Sell_Condition_2 = (p_close <maVal[1]);                         // Previous price closed below MA-8
   bool Sell_Condition_3 = (adxVal[0]>Adx_Min);                         // Current ADX value greater than minimum (22)
   bool Sell_Condition_4 = (plsDI[0]<minDI[0]);                         // -DI greater than +DI
   
 //--- Putting all together
   if(Sell_Condition_1 && Sell_Condition_2)
       {
         if(Sell_Condition_3 && Sell_Condition_4)
           {
            // any opened Sell position?
            if (Sell_opened) 
            {
                Alert("We already have a Sell position!!!"); 
                return;    // Don't open a new Sell Position
            }
            mrequest.action = TRADE_ACTION_DEAL;                                 // immediate order execution
            mrequest.price = NormalizeDouble(latest_price.bid,_Digits);          // latest Bid price
            mrequest.sl = NormalizeDouble(latest_price.bid + STP*_Point,_Digits); // Stop Loss
            mrequest.tp = NormalizeDouble(latest_price.bid - TKP*_Point,_Digits); // Take Profit
            mrequest.symbol = _Symbol;                                         // currency pair
            mrequest.volume = Lot;                                            // number of lots to trade
            mrequest.magic = EA_Magic;                                        // Order Magic Number
            mrequest.type= ORDER_TYPE_SELL;                                     // Sell Order
            mrequest.type_filling = ORDER_FILLING_FOK;                          // Order execution type
            mrequest.deviation=100;                                           // Deviation from current price
            //--- send order
            OrderSend(mrequest,mresult);

Assim, como fizemos na seção de compra, estamos declarando uma variável do tipo bool para cada uma de nossas condições que devem ser atendidas antes que uma ordem possa ser colocada. Uma variável do tipo boll pode apenas conter VERDADEIRO ou FALSO. Então a nossa estratégia de venda foi quebrada em quatro condições. Se qualquer uma das condições for atendida ou satisfeita, então um valor de VERDADEIRO é armazenado em nossa variável do tipo bool, de modo contrário, um valor de FALSO será armazenado. Vamos olhar para elas uma a uma como nós fizemos para a seção de compra.

   bool Sell_Condition_1 = (maVal[0]<maVal[1]) && (maVal[1]<maVal[2]);

Aqui nós estamos olhando para os valores MA-8 nas barras 0, 1 e 2. Se o valor de MA-8 na barra atual for menor do que seu valor na barra 1 anterior e também o valor de MA-8 na barra 1 for menor do que seu valor na Barra 2, significa que o MA-8 está decrescendo para baixo. Isso satisfaz uma das nossas condições para uma configuração de venda.

   bool Sell_Condition_2 = (p_close <maVal[1]); 

Essa expressão está verificando para ver se o preço de fechamento da barra 1 é menor do que o valor de MA-8 no mesmo período (período da barra 1). Se o preço for menor, então a nossa segunda condição também foi satisfeita, e podemos verificar as outras condições. No entanto, se as duas condições que acabamos de considerar não foram atendidas, então não há necessidade de verificar outras condições. É por isso que decidimos incluir as próximas expressões dentro dessas duas condições iniciais (expressões).

   bool Sell_Condition_3 = (adxVal[0]>Adx_Min); 

Agora queremos verificar se o valor atual de ADX (valor de ADX na barra 0) é maior do que o valor de ADX máximo declarado nos parâmetros de entrada. Se essa expressão for verdadeira, isto é, o valor atual de ADX é maior do que o valor mínimo necessário; também queremos nos certificar de que o valor MinusDI é maior do que o valor plusDI. Isso é o que alcançamos na próxima expressão.

bool Sell_Condition_4 = (plsDI[0]<minDI[0]);

Se todas essas condições forem atendidas, isto é, se elas retornam verdadeiras, então nós queremos nos certificar de que não vamos abrir uma nova posição de compra se nós já possuímos uma. É hora de verificar o valor da variável Buy_opened que declaramos anteriormente em nosso código.

// any opened Sell position?
            if (Sell_opened) 
            {
                Alert("We already have a Sell position!!!"); 
                return;    // Don't open a new Sell Position
            }

Se Sell_opened for verdadeira, nós não queremos abrir outra posição de venda, então, exibimos um alerta para nos informar e aí retornar de forma que o nosso CE irá agora esperar pela próxima variação. No entanto, se Sell_opened for FALSO, então configuramos a nossa ordem de negociação de venda como fizemos para a ordem de compra.

A maior diferença aqui é a forma como calculamos o nosso preço stop loss e o preço take profit. Também já que estamos vendendo, nós vendemos no preço de cotação; é por isso que utilizamos o nosso tipo MqlTick variável latest_price para conseguir o preço de cotação mais recente. O outro tipo aqui, conforme explicado anteriormente, é ORDER_TYPE_SELL.

Também aqui, utilizamos a função NormalizeDouble para o preço de compra, valor do stop loss (parar perdas) e take profit (obter lucros), é boa prática sempre normalizar estes preços ao número de dígitos do par de moedas antes de enviar para o servidor de negociação.

Assim, como fizemos com a nossa ordem de compra, precisamos também verificar se a nossa ordem de venda foi bem sucedida ou não. Então utilizamos a mesma expressão do que na nossa ordem de compra.

         if(mresult.retcode==10009 || mresult.retcode==10008) //Request is completed or order placed
           {
            Alert("A Sell order has been successfully placed with Ticket#:",mresult.order,"!!");
           }
         else
           {
            Alert("The Sell order request could not be completed -error:",GetLastError());
            ResetLastError();
            return;
           }
        }


3. Depurando e testando o nosso consultor especialista

Nesse momento, precisamos testar o nosso CE pra sabermos se a nossa estratégia funciona ou não. Também é possível que haja um ou dois erros no código do nosso CE. Isso será descoberto no próximo passo.

3.1 Depurando

Depurar o nosso código nos ajuda a ver como o nosso código se desempenha linha por linha (se nós configurarmos pontos de parada (breakpoints)) e lá podemos notar quaisquer erros ou bugs no nosso código e rapidamente fazer as correções necessárias antes de utilizar nosso código em uma negociação real.

Aqui vamos passar pelo processo passo a passo de depurar o nosso consultor especialista, primeiramente, configurando breakpoints e em segundo lugar, sem breakpoints. Para fazer isso, certifique-se de que você não fechou o editor. Primeiramente, vamos selecionar o gráfico que queremos utilizar para testar o nosso CE. Na barra do menu do editor, clique em ferramentas e clique em Opções como mostrado abaixo:

Figura 8. Configurando opções de depuração

Figura 8. Configurando opções de depuração

Uma vez que a janela de opções aparece, selecione o par de moeda, e o período/cronograma para utilizar e clique no botão OK:

Figura 9. Janela de opções do depurador

Antes de iniciarmos o depurador, vamos configurar os breakpoints. Breakpoints nos permitem monitorar o comportamento/desempenho do nosso código em certas localizações ou linhas selecionadas. Ao invés de percorrer todo o código de uma vez, o depurador para sempre que ver um breakpoint, esperando a sua próxima ação. Com isso, seremos capazes de analisar o nosso código e monitorar seu comportamento conforme ele alcança cada conjunto de breakpoints. Nós também seremos capazes de avaliar os valores de algumas das nossas variáveis para ver se as coisas de fato estão da forma como foram visionadas.

Para inserir um breakpoint, vá na linha em seu código na qual você quer inserir o breakpoint. No lado da mão esquerda, no campo cinza perto da borda da linha do código, clique duas vezes e você verá um pequeno botão azul redondo com um quadrado branco dentro dele. Ou alternativamente, posicione o cursor do seu mouse em qualquer lugar na linha do código onde você quer que o breakpoint apareça e aperte F9. Para remover o breakpoint, pressione F9 novamente ou clique nele duas vezes.

Figura 10. Configurando um breakpoint

Figura 10. Configurando um breakpoint

Para o nosso código, nós vamos configurar breakpoints em cinco linhas diferentes.

Eu também identificarei eles de 1 a 5 para o bem da explicação.

Para continuar, configure o breakpoint nas sete linhas de código como mostrado na figura abaixo. O breakpoint 1 é o que nós criamos acima.

Figura 11. Configurando breakpoints adicionais

Figura 11. Configurando breakpoints adicionais

Uma vez que tenhamos terminado de configurar os nossos breakpoints, estamos agora prontos para começar a depurar o nosso código.

Para iniciar o depurador, pressione F5 ou clique no botão verde na barra de ferramentas do MetaEditor.

Figura 12. Iniciando o depurador

Figura 12. Iniciando o depurador

A primeira coisa que o editor faz é compilar o código, se houver qualquer erro no ponto, ele irá exibi-lo e se não houver erro, ele irá lhe avisar que o código foi compilado com sucesso.

Figura 13. Relatório de compilação

Figura 13. Relatório de compilação

Por favor observe que o fato de que o código compilou com sucesso não significa que não existem erros no seu código. Dependendo de como o código é escrito, podem haver erros de execução. Por exemplo, se qualquer uma das nossas expressões não avalia corretamente devido a qualquer pequeno descuido, o código irá compilar corretamente porém pode não executar corretamente. Muita conversa, vamos ver em ação...

Uma vez que o depurador terminou de compilar o código, ele leva você para o terminal de comercialização, e anexa o CE ao gráfico que você especificou nas configurações das opções do MetaEditor. Ao mesmo tempo, ele mostra para você a seção de parâmetros de entrada do EA. Já que não ainda não estamos ajustando nada, apenas clique no botão OK.

Figura 14. Parâmetros de entrada do consultor especialista para depuração

Figura 14. Parâmetros de entrada do consultor especialista para depuração

Você vai agora ver o CE claramente no canto superior direito do gráfico.

Uma vez que ele inicia o OnTick(), ele irá parar assim que ele chegar no nosso breakpoint 1.

Figura 15. Depurador para no primeiro breakpoint

Figura 15. Depurador para no primeiro breakpoint

Você notará uma seta verde na linha do código. Isso lhe diz que a linha de código anterior foi executada; estamos agora prontos para executar a linha atual.

Deixe-me fazer algumas explicações antes de prosseguirmos. Se você olhar a barra de ferramentas do editor, você observará que os três botões com setas curvas que estavam anteriormente em cinza agora estão ativados. Isso é porque estamos agora executando o depurador. Esses botões são utilizados para percorrer o nosso código (entrar, passar ou cima ou sair)

Figura 16. Comando entrar

Figura 16. Comando entrar

O entrar é utilizado para ir de um passo na execução do programa para o próximo passo, entrando em qualquer função chamada dentro daquela linha de código. Clique no botão ou pressione F11 para invocar o comando (utilizaremos esse comando em nossa depuração passo a passo para o nosso código).

Figura 17. Comando passar por cima

Figura 17. Comando passar por cima

O passar por cima, por outro lado não entra na chamada função dentro daquela linha de código. clique no botão ou pressione F10 para invocar o comando

Figura 18. Comando sair

Figura 18. Comando sair

Par executar o passo do programa que é um nível acima, você clica nesse botão ou pressiona Shift+F11.

Também, na parte inferior do editor, você verá a janela da caixa de ferramentas. A aba de depuração nessa janela possui os seguintes cabeçalhos:

  • Arquivo: Isso exibe o nome do arquivo chamado;
  • Função: Isso exibe a função presente do arquivo chamado;
  • Linha: Isso exibe o número da linha de código no arquivo do qual a função é chamada;
  • Expressão: Isso é onde você pode digitar o nome de qualquer expressão/variável que você está interessado em monitorar do nosso código;
  • Valor: Isso irá exibir o valor da expressão/variável que nós digitamos na área de expressão;
  • Tipo: Isso exibirá o tipo de dados da expressão/variável que foi monitorada.

De volta ao processo de depuração...

A próxima coisa que queremos fazer é agora inserir as variáveis/expressões do nosso código que nós estamos interessados em monitorar. Certifique-se de apenar monitorar variáveis/expressões que realmente importam para o seu código. Por exemplo, monitoraremos as seguintes:

  • Old_Time (tempo antigo da barra);
  • New_Time[0] (tempo atual da barra);
  • IsNewBar (bandeira que indica a nova barra);
  • Mybars (total de barras no histórico) – O nosso CE depende disso.

Você pode adicionar outras como os valores ADX, os valores MA-8, etc.

Para adicionar a expressão/variável, clique duas vezes sob a área de expressões ou cliquei direito sob a área de expressões e selecione Adicionar como mostrado na figura acima.

Digite a expressão/variável para monitorar ou observar.

Figura 19. A janela de observação das expressões

Figura 19. A janela de observação das expressões

Digite todas as variáveis/expressões necessárias...

Figura 20. Adicionando expressões ou variáveis para observar

Figura 20. Adicionando expressões ou variáveis para observar

Se a variável não foi declarada ainda, seu tipo é "identificador desconhecido" (exceto as variáveis estáticas).

Agora, vamos prosseguir...

Figura 21. Comando entrar em ação

Figura 21. Comando entrar em ação

Clique no botão entrar ou pressione F11 e observe o que acontece. Continue pressionando esse botão ou F11 até que você chegue no breakpoint no 2, continue até que você chegue no breakpoint no 4 como mostrado abaixo e observe a janela de observação de expressões.


Figura 22. Observando as expressões ou variáveis

Figura 22. Observando as expressões ou variáveis

Figura 23. Observando as expressões ou variáveis

Figura 23. Observando as expressões ou variáveis

Figura 24. Observando as expressões ou variáveis

Figura 24. Observando as expressões ou variáveis

Uma vez que há uma nova variação, ela retornará para a primeira linha do código na função OnTick(). E todos os valores das nossas variáveis/expressões agora serão reiniciados porque essa é uma nova variável se qualquer um deles for declarado como uma variável estática. Em nosso caso possuímos uma variável estática Old_Time.

Figura 25. Valores das variáveis no evento NewTick

Figura 25. Valores das variáveis no evento NewTick

Para revisar o processo novamente, continue pressionando a tecla F11 e continue monitorando as variáveis na janela de observação de expressões. Você pode parar o depurador e então remover todos os breakpoints.

Como podemos ver, em modo de depuração ele publica a mensagem "Nós temos uma nova barra aqui...".

Figura 26. Expert Advisor imprime a mensagem no modo de depuração

Figura 26. Expert Advisor imprime a mensagem no modo de depuração

Iniciando o processo de depuração novamente; mas dessa vez sem breakpoints. Fique observando a cada variação e se qualquer uma das nossas condições de compra/venda for satisfeita, ele oferecerá uma negociação e já que escrevemos o nosso código para nos dizer se uma ordem é feita com sucesso ou não, veremos um alerta.

Figura 27. Expert Advisor oferece uma negociação durante a depuração

Figura 27. Expert Advisor oferece uma negociação durante a depuração

Acho que você pode deixar o CE (EA) trabalhando por mais alguns minutos enquanto você toma um café. Quando você estiver de volta e tiver ganhado algum dinheiro (brincadeira), então clique no botão PARAR (vermelho) no MetaEditor para parar a depuração.

Figura 28. Parando o depurador

Figura 28. Parando o depurador

O que nós fizemos de fato aqui é ver que o nosso CE apenas procura uma operação de comercialização na abertura de uma nova barra e que o nosso CE funciona de fato. Há muito espaço para ajustes ao código do nosso CE.

Preciso deixar claro, nesse ponto, que o terminal de comercialização deve ser conectado à internet, caso contrário, a depuração não funcionará porque o terminal não é capaz de comercializar.

3.2 Testando a nossa estratégia de CE

Agora nesse momento queremos testar o nosso CE utilizando o verificador de estratégia integrado no terminal de comercialização. Para iniciar o testador de estratégia, pressione CONTROL+R ou clique no menu Visualizar na barra do menu do terminal e clique em verificador de estratégia como mostrado abaixo.

Figura 26. Iniciando o teste da estratégia

Figura 26. Iniciando o teste da estratégia

O verificador (verificador de estrategia) é mostrado na parte mais baixa do terminal. Para você ver todas as configurações do verificador, é preciso expandir/redimensionar ele. Para fazer isso, mova o ponteiro do seu mouse para a ponta mostrada pela seta vermelha (como mostrado abaixo):

Figura 27. Janela do verificador de estratégia

Figura 27. Janela do verificador de estratégia

O ponteiro do mouse muda para uma seta com ponta dupla, segure o mouse e arraste a linha para cima. Pare quando você descobrir que você pode ver tudo na aba de configurações.

Figura 28. Aba de configurações do verificador de estratégia

Figura 28. Aba de configurações do verificador de estratégia

  1. Selecione o CE que você quer testar.
  2. Selecione o par de moedas que você quer utilizar para o teste.
  3. Selecione um período/cronograma para utilizar para o teste.
  4. Selecione o período personalizado e configure as datas em 5.
  5. Configure as datas para o período personalizado a ser utilizado no teste.
  6. Execução é normal.
  7. Selecione a quantidade depositada em USD a ser utilizada para o teste.
  8. Configure a otimização para desabilitar (nós não estamos otimizando agora, só queremos testar).
  9. Clique nesse botão quando você estiver pronto para começar.

Antes de clicarmos no botão iniciar, vamos olhar as outras abas no verificador.

Aba agentes

O processador utilizado pelo verificador para o teste. Dependendo do tipo de processador do seu computador. O meu é apenas um processador de um (1) núcleo.

Figura 29. Aba agentes do verificador de estratégia

Figura 29. Aba agentes do verificador de estratégia

Uma vez no agente, você verá algo similar a figura abaixo:

Figura 30. A aba agentes do verificador de estratégia durante um teste

Figura 30. A aba agentes do verificador de estratégia durante um teste

Aba jornal

É aonde todos os eventos ocorrendo durante o período de teste são exibidos

Figura 31. Aba jornal do verificador de estratégia mostrando atividades de comercialização

Figura 31. Aba jornal do verificador de estratégia mostrando atividades de comercialização

Aba de entradas

Aqui é onde você pode especificar os parâmetros de entrada para o CE.

Figura 32. Aba entradas do verificador de estratégia

Figura 32. Aba entradas do verificador de estratégia

Se nós estamos otimizando o nosso CE, então nós precisaremos configurar os valores na área circulada.

  • O Start é o valor que você quer que comece com o verificador.
  • O Step é a taxa de incremento para o valor que você selecionou, e
  • O Stop é o valor no qual o verificador irá parar de incrementar o valor para aquele parâmetro.

No entanto, em nosso caso, não estamos otimizando o nosso CE, então não precisaremos tocar nisso por enquanto.

Uma vez que tudo estiver pronto, podemos agora voltar a aba de Configurações e clicar no botão iniciar. Então o verificador começa o seu trabalho. Tudo que você precisa fazer agora é ir e tomar outra xícara de café, se quiser, ou, se você for como eu, pode querer monitorar cada evento e, então, ir para a aba jornal.

Aba gráfico

Uma vez que você começar a ver mensagens sobre ordens sendo enviadas para a aba jornal, você pode querer voltar-se para uma nova aba chamada gráfico que acabou de ser criada. Uma vez que você mude para a aba gráfico, verá o gráfico aumentando ou diminuindo cada vez mais conforme o caso dependendo do resultado das suas comercializações.

Figura 33. O resultado do gráfico para o teste do consultor especialista

Figura 33. O resultado do gráfico para o teste do consultor especialista

Aba resultados

Uma vez que o teste estiver completo, você verá outra aba chamada resultados. Mude para a aba resultados e você verá o resumo do teste que nós acabamos de fazer.

 Figura 34. A aba dos resultados do verificador de estratégia mostrando o resumo dos resultados do teste

Figura 34. A aba dos resultados do verificador de estratégia mostrando o resumo dos resultados do teste

Você pode ver o lucro bruto total, lucro líquido, comercializações totais, comercializações de perda total e muito mais. É muito interessante ver que temos cerca de out USD 1,450.0 dentro do período que foi selecionado para o nosso teste. Ao menos temos um poco de lucro.

Deixe-me tornar uma coisa muito clara para você aqui. Você descobrirá que as configurações para os parâmetros CE que você vê no verificador de estratégia é diferente das configurações iniciais nos parâmetros de entrada do CE. Eu acabei de demonstrar para você que é possível mudar qualquer um desses parâmetros de entrada para conseguir o melhor do seu CE. Ao invés de utilizar um período de 8 cada para a média móvel e ADX, eu mudei para 10 para a média móvel e 14 para o ADX. Eu também mudei a stop loss de 30 para 35. Por último, mas não menos importante, decidi utilizar um cronograma de 2 horas. Lembre-se, esse é o verificador de estratégia.

Se você quer visualizar um relatório completo do teste, então clique direito em qualquer local da aba resultados, você verá um menu. Desse menu, selecione ‘Salvar e relatar’.

Figura 35. Salvando o resultado do teste

Figura 35. Salvando o resultado do teste

A janela de diálogo salvo aparecerá, digite um nome para o seu relatório (se quiser, caso contrário deixe o nome padrão) e clique no botão salvar. Todo o relatório será salvo no formato HTML para você.

Para visualizar o gráfico para o teste que foi realizado, clique em abrir gráfico e você verá o gráfico exibido.

Figura 36. O gráfico mostrando o teste

Figura 36. O gráfico mostrando o teste

Pronto, escrevemos e testamos o nosso CE com sucesso e agora possuímos um resultado para trabalhar. Você pode agora voltar para a aba de configurações de estratégia do verificador de estratégia e testar outros cronogramas/períodos.

Tarefa

Quero que você conduza o teste utilizando diferentes pares de moedas, diferentes cronogramas, diferentes stop loss, diferentes take profit e veja como o CE se desempenha. Você pode até tentar a nova média móvel e os valores ADX. Como eu disse anteriormente, isso é a essência do verificador de estratégia. Eu também gostaria que você compartilhasse os seus resultados comigo.

Conclusão

Nesse guia passo a passo nós conseguimos visualizar as etapas básicas necessárias para escrever um consultor especialista simples baseado em uma estratégia de comercialização desenvolvida. Nós também vimos como verificar o nosso CE para erros utilizando o depurador. Também discutimos como testar o desempenho do nosso CE utilizando o verificador de estratégia. Com isso, conseguimos ver o poder e robustez da nova linguagem MQL5. O nosso CE ainda não é perfeito ou completo já que muitos outros ajustes precisam ainda ser feitos de forma a utilizar ele para comercializações reais.

Ainda há mais a aprender e eu quero que você leia o artigo várias vezes juntamente com o manual do MQL5, e tente tudo que você aprendeu nesse artigo, eu posso lhe assegurar que você será um ótimo desenvolvedor de CE em um futuro próximo.

Feliz programação.

Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/100

Arquivos anexados |
my_first_ea.mq5 (11.86 KB)
Últimos Comentários | Ir para discussão (16)
Arthur Vieira
Arthur Vieira | 6 ago 2020 em 23:01
Sella170:
Alguém sabe se posso utilizar esse EA para mini-índice ou mini-dólar ?
Sim, é possível utilizar o EA com o mouse arrastando para gráfico ou menu > inserir > Expert advisor > {nomedoseurobo}.

Espero esclarecido sua dúvida.

Vitor Gama Barreto
Vitor Gama Barreto | 21 dez 2020 em 18:14

Olá pessoal, bom dia.

Estou no "Testador de Estratégia" com este arquivo do final do Guia e ele aparece o seguinte erro:

*o erro já foi conversado aqui neste artigo, porém nenhuma solução escrita deu certo comigo e preciso de suas ajudas.


OF      0       12:44:39.040    EA_ADX (XAUUSD,H1)      2020.01.03 19:00:00   Alert: The Buy order request could not be completed -error:4756
ED      2       12:44:39.043    Trades  2020.01.03 20:00:00   failed market buy 0.1 XAUUSD sl: 1549.09 tp: 1549.16 [Invalid stops]
JP      0       12:44:39.043    EA_ADX (XAUUSD,H1)      2020.01.03 20:00:00   Alert: The Buy order request could not be completed -error:4756
ER      2       12:44:39.049    Trades  2020.01.03 21:00:00   failed market buy 0.1 XAUUSD sl: 1549.90 tp: 1549.97 [Invalid stops]
MR      0       12:44:39.049    EA_ADX (XAUUSD,H1)      2020.01.03 21:00:00   Alert: The Buy order request could not be completed -error:4756
PP      2       12:44:39.053    Trades  2020.01.03 22:00:00   failed market buy 0.1 XAUUSD sl: 1548.16 tp: 1548.23 [Invalid stops]


O arquivo é o do final do Guia.

Como resolver este problema?

Régis Amaral
Régis Amaral | 28 fev 2021 em 17:04
Há um erro na lógica do algoritmo aqui:
 //--- Do we have positions opened already?
     bool Buy_opened= false ;   // variable to hold the result of Buy opened position
     bool Sell_opened= false ; // variable to hold the result of Sell opened position
    
     if ( PositionSelect ( _Symbol ) == true )   // we have an opened position
    {
         if ( PositionGetInteger ( POSITION_TYPE ) == POSITION_TYPE_BUY )
         {
            Buy_opened = true ;   //It is a Buy
         }
         else if ( PositionGetInteger ( POSITION_TYPE ) == POSITION_TYPE_SELL )
         {
            Sell_opened = true ; // It is a Sell
         }
    }

Como PositionSelect seleciona a primeira posição para _Symbol, se o EA abrir uma posição de compra, a variável Sell_opened sempre será falsa enquanto a posição de compra estiver aberta, fazendo com que várias posições de venda sejam realizadas sempre que houver um sinal de venda.

Para corrigir isso e saber se há posições de compra e venda abertas simultaneamente, a seguinte lógica pode ser usada:

 //--- Do we have positions opened already?
   bool Buy_opened = false ;   // variable to hold the result of Buy opened position
   bool Sell_opened = false ; // variables to hold the result of Sell opened position

   if ( PositionSelect ( _Symbol ) == true ) // we have an opened position
   {
       for ( int i= 0 ;i< PositionsTotal ();i++){
	 
         PositionSelectByTicket ( PositionGetTicket (i));

         if ( PositionGetInteger ( POSITION_TYPE ) == POSITION_TYPE_SELL )
         {
            Sell_opened = true ; // It is a Sell
         } else if ( PositionGetInteger ( POSITION_TYPE ) == POSITION_TYPE_BUY )
         {
            Buy_opened = true ; //It is a Buy
         }
      }
   
Régis Amaral
Régis Amaral | 28 fev 2021 em 17:35
Vitor Gama Barreto:

Olá pessoal, bom dia.

Estou no "Testador de Estratégia" com este arquivo do final do Guia e ele aparece o seguinte erro:

*o erro já foi conversado aqui neste artigo, porém nenhuma solução escrita deu certo comigo e preciso de suas ajudas.



O arquivo é o do final do Guia.

Como resolver este problema?

Verifique o preço de venda que está solicitando, para isso use breakpoint para depurar as variáveis mrequest.price, mrequest.sl e mrequest.tp 

Tente mudar

mrequest.type_filling = ORDER_FILLING_FOK;                               // Order execution type

para:

mrequest.type_filling = ORDER_FILLING_IOC;                               // Order execution type


Alguns papeis tem regras de Stop Loss minimo, leia mais aqui: https://www.mql5.com/pt/forum/319474

Jorge Luis Correa De Menezes
Jorge Luis Correa De Menezes | 21 out 2021 em 15:18

Embora em alguns indicadores o tamanho do MQL5 esteja menor que o MQL4,

a impressão que dá é que o MQL5 ficou mais complicado e poluido que o MQL4

Criar um quadro de informação utilizando classes de biblioteca padrão e o Google Chart API Criar um quadro de informação utilizando classes de biblioteca padrão e o Google Chart API
A linguagem de programação MQL5 foca principalmente na criação dos sistemas de negociação automatizada e instrumentos complexos da análise técnica. Mas, fora isso, ela permite criar sistemas de informação interessantes para rastrear situações de mercado e fornece uma conexão de retorno com o negociante. O artigo descreve os componentes da Biblioteca Padrão MQL5, e mostra exemplos de seu uso na prática para alcançar estes objetivos. Ela também demonstra um exemplo para utilizar o Google Chart API para criação de gráficos.
Criando indicadores de ponto no MQL5 Criando indicadores de ponto no MQL5
Neste artigo, consideraremos a criação de dois indicadores: o indicador de tick, que plota o gráfico de tick do indicador de vela de tick e preço, que plota velas com o número específico de ticks. Cada um dos indicadores escreve os preços de entrada em um arquivo e usa os dados salvos após o reinício do indicador (estes dados também podem ser usados por outros programas).
Funções para gerenciamento de dinheiro em um conselheiro especialista Funções para gerenciamento de dinheiro em um conselheiro especialista
O desenvolvimento das estratégias de negócio foca principalmente em buscar padrões para entrar e sair do mercado, bem como manter posições. Se formos capazes de formalizar alguns padrões em regras para negociação automatizada, então, o negociante enfrenta o problema de cálculo do volume das posições, o tamanho das margens, bem como manter um nível seguro dos fundos de hipoteca para garantir posições abertas no modo automatizado. Neste artigo, usaremos a linguagem do MQL5 para construir exemplos simples para realizar estes cálculos.
Estilos de desenhos no MQL5 Estilos de desenhos no MQL5
Existem 6 estilos de desenho no MQL4 e 18 estilos de desenho no MQL5. Então pode valer a pena escrever um artigo para introduzir os estilos de desenhos do MQL5. Neste artigo consideraremos os detalhes de estilos de desenho no MQL5. Além disso, criaremos um indicador para demonstrar como usar esses estilos de desenhos e refinar a diagramação.