MQL MQL5 – Tutorial 1


Variáveis Externas

A palavra-chave extern é usado para declarar identificadores de variáveis como identificadores da classe de armazenamento estática (estático) com tempo de vida global. Estas variáveis existem a partir do inicio do programa e a memória para elas é alocada e inicializada imediatamente após o inicio do programa.

Você pode criar programas que consistem de múltiplos arquivos fontes; neste caso a diretiva para o preprocessador #include é usada. Variáveis declaradas como extern com o mesmo tipo e identificador podem existir em arquivos fontes diferentes de um projeto.

Ao compilar o projeto inteiro, todas as variáveis extern com o mesmo tipo e identificador são associados com uma parte da memória do conjunto (pool) de variáveis globais. Variáveis extern são úteis para compilação separada de arquivos fontes. Variáveis externas podem ser inicializadas, mas apenas uma vez – a existência de várias variáveis externas inicializadas do mesmo tipo e com o mesmo identificador é proibida.

Também Veja

Tipos de DadosEncapsulamento e Extensibilidade de TiposInicialização de VariáveisVisibilidade Escopo e Tempo de Vida de VariáveisCriando e Excluindo Objetos

https://www.mql5.com/pt/docs

Manual MQL5

A MetaQuotes Language 5 (MQL5) é uma linguagem de programação para indicadores técnicos, para robôs de negociação e para aplicativos auxiliares, providenciando automatização da negociação nos mercados financeiros. A MQL5 é uma linguagem moderna de alto nível desenvolvida pela MetaQuotes Software Corp. para a sua própria plataforma de negociação. A sintaxe da linguagem é tão próxima quanto possível da C++ e permite escrever programas no estilo da programação orientada a objetos (POO).

Para escrever programas em MQL5, a plataforma de negociação vem com o ambiente de desenvolvimento do MetaEditor que tem todo um conjunto de modernas ferramentas para escrita de códigos, incluindo modelos, trechos de código, depuração, criação de perfis, preenchimento automático e armazenamento de versão integrado MQL5 Storage.

O suporte e o desenvolvimento da linguagem são realizados no site da MQL5.community. Nele existe uma extensa biblioteca de códigos gratuitos e muitos artigos. Estes artigos abrangem todos os tópicos da negociação moderna: redes neurais, estatísticas e análise, negociação de alta frequência, arbitragem, testes e otimização de estratégias de negociação, uso de robôs para automatizar a negociação e muito mais.

Traders e desenvolvedores de programas MQL5 podem interagir no fórum, realizar encomendas no serviço Freelance, comprar e vender programas seguros no Mercado – uma loja de aplicativos prontos para negociação automática.

A linguagem MQL5 contém funções de negociação especializadas e manipuladores de ventos pré-definidos para escrita de Expert Advisors. Os Expert Advisors gerenciam automaticamente os processos de negociação com base nas regras comerciais estabelecidas neles. Você também pode criar em MQL5 seus próprios indicadores técnicos, scripts e bibliotecas de funções.

O manual MQL5 contém – divididas por categorias – funções, operações, palavras reservadas e outras construções da linguagem. Além disso, permite encontrar a descrição de cada elemento da linguagem. Também o manual descreve as classes da Biblioteca padrão para criar estratégias de negociação, painéis de controle, gráficos personalizados e trabalhar com arquivos.

Separadamente do manual, o CodeBase dispõe de uma biblioteca de análise numérica ALGLIB que permite resolver inúmeros problemas matemáticos.
 

Tipos de aplicativos em MQL5

A fim de executar tarefas específicas para automatizar operações de negociação, os programas MQL5 são divididos em quatro tipos especializados:

  • Expert Advisor – sistema de negociação automática que está ligado a um gráfico específico. O Expert Advisor contém as funções manipuladoras dos eventos predefinidos que, ao ocorrerem, desencadeiam os respectivos elementos da estratégia de negociação. Esses eventos podem ser a inicialização e a desinicialização do programa, a chegada de um novo tick, a ativação do temporizador, a mudança no livro de ofertas, nos eventos do gráfico e nos eventos do usuário.
    O Expert Advisor pode, além de calcular sinais de negociação segundo as regras estabelecidas, realizar trades automaticamente na conta de negociação, direcionando-os diretamente para o servidor de negociação. Os Expert Advisor são armazenados no <diretório_do_terminal>\MQL5\Experts.
  • Indicador personalizado – indicador técnico escrito pelo usuário, além dos indicadores já integrados na plataforma de negociação. Os indicadores personalizados, assim como os indicadores embutidos, não podem ser trocados automaticamente e se destinam apenas à implementação de funções analíticas. Os indicadores personalizados podem usar os valores de outros indicadores em seus cálculos, e eles próprios podem ser chamados nos próprios Expert Advisors.
    Os indicadores personalizados são armazenados no <diretório_do_terminal>\MQL5\Indicators.
  • Script – programa projetado para a execução única de certa ação. Ao contrário dos Expert Advisors, os scripts não lidam com eventos, exceto com aqueles que são de inicialização e desinicialização. Para executar o script em seu código, deve haver uma função manipulador OnStartOs scripts são armazenados no <diretório_do_terminal>\MQL5\Scripts.
  • Serviço — um programa que, ao contrário de indicadores, EAs e scripts, não estão vinculados a um gráfico para o seu trabalho. Como os scripts, os serviços processam apenas eventos de inicialização. Para executar o serviço em seu código, deve estar presenta uma função manipulador OnStart. Os serviços aceitam apenas o evento o Start, mas podem enviar aos gráficos eventos personalizados usando EventChartCustom. Os serviços são armazenados no diretório <diretório_do_terminal>\MQL5\Services.
  • Biblioteca – biblioteca de funções personalizadas projetada para armazenar e distribuir blocos – usados com frequência – de programas do usuário. As bibliotecas não podem ser executadas por conta própria.
    As bibliotecas são armazenadas no <As bibliotecas não podem ser executadas por conta própria.>\MQL5\Libraries
  • Arquivo de inclusão – código fonte de blocos – usados com frequência – de programas do usuário. Esses arquivos podem ser incluídos nos códigos fonte de Expert Advisors, de scripts, de indicadores personalizados e de bibliotecas durante fase de compilação. Devido à sobrecarga adicional ao chamar funções de biblioteca, é melhor usar arquivos de inclusão do que bibliotecas.
    Arquivos de inclusão podem estar no mesmo diretório que o código fonte, neste caso é usada a diretiva #include com aspas duplas. Outro local para armazenar os arquivos de inclusão está no <diretório_do_terminal>\MQL5\Include, neste caso, é utilizada a diretiva #include com colchetes angulares.

Elementos Básicos da Linguagem

A Linguagem MetaQuotes 5 (MQL5) é uma linguagem orientado a objetos de alto nível destinado para escrever estratégias de negociação automatizadas, e indicadores técnicos customizados para a a análise de vários mercados financeiros. Ele permite não somente escrever uma variedade de sistemas especialistas, concebidos para operar em tempo real, mas também criar suas próprias ferramentas gráficas para ajudar você a tomar decisões de negociação (trade).

MQL5 é baseado no conceito da popular linguagem de programação C++. Em comparação com MQL4, a nova linguagem tem agora enumeradoresestruturasclasses e manipulação de evento. Ao aumentar o número de tipos principais incorporados, a interação dos programas executáveis em MQL5 com outros aplicativos através de dll é agora tão fácil quanto possível. A sintaxe do MQL5 é similar à sintaxe do C++, e isso faz com que seja fácil traduzir para ele programas de linguagens modernas de programação.

Para ajudar você a estudar a linguagem MQL5, todos os tópicos estão agrupados nas seguintes seções:

Sintaxe

Quanto à sintaxe, a linguagem MQL5 para programação de estratégias de negociação é muito similar à linguagem de programação C++, com exceção de alguns recursos:

  • ausência de aritmética com endereço
  • ausência do operador goto
  • um enumerador anônimo não pode ser declarado
  • ausência de herança múltipla

Também Veja

EnumeradoresEstruturas e ClassesHerança

Comentários

Comentários de múltiplas linhas começam com o par de símbolos /* e terminam com o par */. Este tipo de comentário não pode ser aninhado. Comentários de linha única começam com o par de símbolos // e terminam com o caractere newline, eles podem ser aninhados em outros comentários de múltiplas linhas. Comentários são permitidos em todos os lugares onde os espaços são permitidos, eles podem conter qualquer número de espaços.

Exemplos:

//— Comentário de linha única
/*  Comentário
   de múltiplas         // Comentário de linha única aninhada
   linhas
*/

Identificadores

Identificadores são usados como nomes de variáveis e funções. O comprimento de um identificador não pode exceder 63 caracteres.

Caracteres permitidos na formação de um identificador: números 0-9, as letras maiúsculas e minúsculas latinas a-z e A-Z, reconhecidos como caracteres diferentes, o caractere sublinhado (_). O primeiro caractere não pode ser um dígito.

O identificador não deve coincidir com uma palavra reservada.

Exemplos:

NAME1 namel Total_5 Paper

Também Veja

VariáveisFunções

Variáveis

Declarando Variáveis

Variáveis devem ser declaradas antes de serem usadas. Nomes únicos são usados para identificar variáveis. Para declarar uma variável, você deve especificar o seu tipo e um único nome. Declaração de variável não é um operador.

Os tipos simples são:

  • char, short, int, long, uchar, ushort, uint, ulong — inteiros;
  • color — inteiro representando a cor-RGB (red, green, blue);
  • datetime — a data e hora, um inteiro sem sinal (unsigned) contendo o número de segundos desde a 0 hora de 1 de Janeiro de 1970;
  • bool — valores booleanos true e false;
  • double — número de ponto flutuante de dupla precisão;
  • float — número de ponto flutuante de precisão simples;
  • string — strings de caractere.

Exemplos:

string szInfoBox;
int    nOrders;
double dSymbolPrice;
bool   bLog;
datetime tBegin_Data   = D’2004.01.01 00:00′;
color    cModify_Color = C’0x44,0xB9,0xE6′;

Tipos compostos ou complexos:

Estruturas são tipos de dados compostos construídos usando outros tipos.

struct MyTime
  {
   int hour;    // 0-23
   int minute;  // 0-59
   int second;  // 0-59
  };

MyTime strTime; // Variável previamente declarada da estrutura MyTime

Não se pode declarar variáveis do tipo estrutura até que se declare a estrutura.

Arrays

Array é uma seqüência indexada de dados de tipos idênticos:

int    a[50];       // Array de uma dimensão de 50 inteiros.
double m[7][50];    // Array de duas dimensões de sete arrays,
                    // cada um deles constituído de 50 números.
MyTime t[100];      // Array contendo elementos como MyTime

Somente um inteiro pode ser um índice de array. Não são permitidos arrays com mais que quatro dimensões. A numeração de elementos de um array começa com 0. O último elemento de um array de uma dimensão tem o número do índice que é 1 a menos que o tamanho do array. Isso significa que chamar pelo último elemento de um array constituído de 50 aparecerá como a[49]. O mesmo acontece a arrays multidimensionais: Um dimensão é indexada de 0 ao tamanho da dimensão menos 1. O último elemento de um array de duas dimensões do exemplo aparecerá como m[6][49].

Arrays estáticos não podem ser representados como séries de tempo, isto é, a funçãoArraySetAsSeries(), que defini o acesso aos elementos de um array do fim para o começo, não pode ser aplicado a eles. Se você desejar ter acesso a um array da mesma forma que uma série de tempo, use o objeto de array dinâmico.

Se houver uma tentativa de acesso fora da faixa do array, a execução do subsistema gerará um erro crítico e o programa será interrompido.

Especificadores de Acesso

Especificadores de acesso definem como o compilador pode acessar variáveis, membros de estruturas ou classes.

O especificador const declara uma variável como uma constante, e não permite modificar esta variável durante o tempo de execução. Uma inicialização simples de uma variável é permitida ao declará-la.

Exemplo:

int OnCalculate (const int rates_total,      // tamanho do array price[]
                 const int prev_calculated,  // barras tratadas na chamada anterior
                 const int begin,            // a partir de onde começam os dados significativos
                 const double& price[]       // array a ser calculado
   );

Para acessar membros de estruturas e classes use os seguintes qualificadores:

  • public — permite acesso irrestrito às variáveis ou métodos da classe
  • protected — permite acesso a partir dos métodos desta classe, bem como a partir dos métodos de classes publicamente herdadas. Outro acesso é impossível;
  • private — permite acesso às variáveis e métodos da classe somente a partir dos métodos da mesma classe.
  • virtual — aplica-se apenas a métodos de classe (não a métodos de estrutura) e diz ao compilador que este método deve se colocado na tabela de funções virtuais da classe.

Classes de Armazenamento

Existem três classes de armazenamento: staticinput e extern. Estes modificadores de classe de armazenamento explicitamente indicam ao compilador que as variáveis correspondentes estão distribuídas em uma área pré-alocada de memória, que é chamada de global pool. Além disso, estes modificadores indicam um processamento especial dos dados da variável. Se uma variável declarada em nível local não for uma variável estática, a memória para tal variável é alocada automaticamente em uma pilha de programa. A liberação de memória alocada para um array não estático (non-static) é também realizada automaticamente quando se passa além da área de visibilidade do bloco em que o array está declarado.

Também Veja

Tipos de DadosEncapsulamento e Extensibilidade de Tipos,Inicialização de VariáveisVisibilidade Escopo e Tempo de Vida de VariáveisCriação e Exclusão de ObjetosMembros Estáticos de uma Classe

Variáveis Locais

Uma variável declarada dentro de uma função é local. O escopo de uma variável local é limitado a região dentro da função na qual a variável está declarada. Uma variável local pode ser inicializada pelo resultado de qualquer expressão. Toda chamada da função inicializa uma variável local. Variáveis locais são armazenadas na área de memória da correspondente função.

Exemplo:

int somefunc()
  {
   int ret_code=0;
   …
   return(ret_code);
  }

Escopo de uma variável é uma parte de um programa, na qual uma variável pode ser referenciada. Variáveis declaradas dentro de um bloco (a nível interno), possuem o bloco como seu escopo. O escopo de bloco começa com a declaração da variável e termina com a chave direita final.

Variáveis locais declaradas no começo de uma função também têm escopo de bloco, bem como parâmetros de função, que são variáveis locais. Qualquer bloco pode conter declarações de variáveis. Se blocos estiverem aninhados e um identificador no bloco externo tiver o mesmo nome que um identificador no bloco interno, o identificador no bloco externo é oculto, até que a operação do bloco interno seja finalizado.

Exemplo:

void OnStart()
  {
//—
   int i=5;      // variável local da função
     {
      int i=10;  // variável de função
      Print(“Dentro do bloco i = “,i); // resultado é i=10;
     }
   Print(“Fora do bloco i = “,i);  // resultado é i=5;
  }

Isso significa que enquanto o bloco interno está rodando, ele vê os valores de seus próprios identificadores locais, e não os valores dos identificadores com nomes idênticos no bloco externo.

Exemplo:

void OnStart()
  {
//—
   int i=5;      // variável local da função
   for(int i=0;i<3;i++)
      Print(“Dentro for i = “,i);
   Print(“Fora do bloco i = “,i);
  }
/* Resultado da execução
Dentro for i = 0
Dentro for i = 1
Dentro for i = 2
Fora do bloco i = 5
*/

Variáveis locais declaradas como estáticas (static) têm escopo de bloco, apesar do fato de elas existirem desde o início do programa.

Pilha (Stack) #

Em todo programa MQL5, uma área especial de memória chamada pilha é alocada para armazenar variáveis locais de funções que são criadas automaticamente. Só uma pilha é alocada para todas as funções e, por padrão, seu tamanho é de 1 MB. Em Advisors e scripts, o tamanho da pilha pode ser gerenciado usando a diretiva de compilação #property stacksize (define o tamanho da pilha em bytes), por padrão, para eles são alocados 8MB.

Variáveis locais estáticas (static) são armazenadas no mesmo lugar onde outras variáveis estáticas e globais são armazenadas – em uma área especial de memória, que existe separadamente da pilha. Variáveis criadas dinamicamente também usam uma área de memória separada da pilha.

A cada chamada de função, um lugar na pilha é alocado para variáveis internas não estáticas. Após sair da função, a memória fica disponível para uso novamente.

Se da primeira função, uma segunda função for chamada, então a segunda função ocupa o tamanho requerido da memória restante da pilha para suas variáveis. Portanto, ao usar funções incluídas, a memória da pilha será seqüencialmente ocupada por cada função. Isso pode levar a uma falta de memória durante uma das chamadas de função, tal situação é chamada de estouro de pilha (stack overflow).

Portanto, para grandes dados é melhor usar memória dinâmica – ao entrar em uma função, aloque a memória que é requerida para as necessidades locais (newArrayResize()), e ao sair da função, libere a memória (deleteArrayFree()).

Também Veja

Tipos de DadosEncapsulamento e Extensibilidade de TiposInicialização de VariáveisVisibilidade Escopo e Tempo de Vida de VariáveisCriando e Excluindo Objetos

Parâmetros Formais

Parâmetros passados para a função são locais. O escopo é de bloco de função. Parâmetros formais devem ter nomes diferentes de variáveis ​​externas e variáveis ​​locais definidas dentro de uma função. Alguns valores podem ser atribuídos a parâmetros formais no bloco de função. Se um parâmetro formal é declarado com o modificador const , seu valor não pode ser modificado dentro da função.

Exemplo:

void func(const int & x[], double y, bool z)
  {
   if(y>0.0 && !z)
      Print(x[0]);
   …
  }

Parâmetros formais podem ser inicializados por constantes. Neste caso, o valor de inicialização é considerado como o valor default (padrão). Parâmetros, próximos ao parâmetro inicializado, também devem ser inicializados.

Exemplo:

void func(int x, double y = 0.0, bool z = true)
  {
   …
  }

Ao chamar tal função, os parâmetros inicializados podem ser omitidos, os valores default serão usados no lugar deles.

Exemplo:

func(123, 0.5);

Parâmetros de tipos simples são passados por valor, isto é, modificações da correspondente variável local deste tipo dentro da função chamada não irá se refletir na função chamadora. Arrays de qualquer tipo e dados do tipo estrutura são passados sempre por referência. Se for necessário proibir modificações no conteúdo de um array ou estrutura, os parâmetros destes tipos devem ser declarados com a palavra-chava const.

Existe a possibilidade de passar parâmetros de tipos simples por referência. Neste caso, modificações nestes parâmetros de dentro da função chamada afetarão as variáveis correspondentes passadas por referência. A fim de indicar que um parâmetro é passado por referência, coloque o modificador & após do tipo do parâmetro.

Exemplo:

void func(int& x, double& y, double & z[])
  {
   double calculated_tp;
   …
   for(int i=0; i<OrdersTotal(); i++)
     {
      if(i==ArraySize(z))       break;
      if(OrderSelect(i)==false) break;
      z[i]=OrderOpenPrice();
     }
   x=i;
   y=calculated_tp;
  }

Parâmetros passados por referência não podem ser inicializados por valores default.

No máximo 64 parâmetros podem ser passado para um função.

Também Veja

Variáveis de EntradaTipo de DadosEncapsulamento e Extensibilidade de TiposInicialização de VariáveisVisibilidade Escopo e Tempo de Vida de VariáveisCriando e Excluindo Objetos

Variáveis Estáticas

A classe de armazenamento estático (static) define uma variável estática. O modificador static é indicado antes do tipo do dado.

Exemplo:

int somefunc()
  {
   static int flag=10;
   …
   return(flag);
  }

Uma variável estática pode ser inicializada por uma constante ou expressão constante correspondente ao seu tipo, diferentemente de uma variável local simples, que pode ser inicializada por qualquer expressão.

Variáveis estáticas existem a partir do momento de execução do programa e são inicializados somente uma vez antes que a função especializada OnInit() seja chamada. Se valores iniciais não forem especificados, variáveis da classe de armazenamento estático assumem valores iniciais zero.

Variáveis locais declarados com a palavra-chave static retém seus valores durante todo o tempo de vida da função. A cada próxima chamada da função, tais variáveis locais contêm os valores que elas tinham durante a chamada anterior.

Quaisquer variáveis em um bloco, exceto parâmetros formais de uma função, podem ser definidas como estáticas. Se uma variável declarada em nível local não for uma variável estática, a memória para tal variável é alocada automaticamente em uma pilha de programa.

Exemplo:

int Counter()
  {
   static int count;
   count++;
   if(count%100==0) Print(“A função Counter tem sido chamada “,count,” tempos”);
   return count;
  }
void OnStart()
  {
//—
   int c=345;
   for(int i=0;i<1000;i++)
     {
      int c=Counter();
     }
   Print(“c =”,c);
  }

Também Veja

Tipos de DadosEncapsulamento e Extensibilidade de TiposInicialização de VariáveisVisibilidade Escopo e Tempo de Vida de VariáveisCriação e Exclusão de ObjetosMembros de Classe Estáticos

Variáveis Globais

Variáveis globais são criadas colocando suas declarações fora de descrições da função. Variáveis globais são definidas no mesmo nível que funções, isto é, elas não são locais de algum bloco.

Exemplo:

int GlobalFlag=10;   // Variável global
int OnStart()
  {
   …
  }

O escopo de variáveis globais é o programa inteiro. Variáveis globais são acessíveis a partir de todas as funções definidas no programa. Elas são inicializadas com zero a menos que um outro valor inicial seja explicitamente definido. Uma variável global pode ser inicializada somente por uma constante, ou expressão de constante que corresponde ao tipo dela.

As variáveis globais são inicializadas apenas uma vez após o programa ser carregado na memória do terminal do cliente e antes da primeira manipulação do evento Init. Para as variáveis globais que representam objetos de classe, durante sua inicialização os construtores correspondentes são chamados. Em scripts, as variáveis globais são inicializadas antes de manipular o evento Start.

Observação: Variáveis declaradas em nível global não devem ser confundidas com variáveis globais do terminal cliente que podem ser acessadas usando as funções GlobalVariable…().

Também Veja

Tipos de DadosEncapsulamento e Extensibilidade de TiposInicialização de VariáveisVisibilidade Escopo e Tempo de Vida de VariáveisCriando e Excluindo Objetos

Variáveis de Entrada

A classe de armazenamento input define uma variável externa. O modificador input é indicada antes do tipo de dado. Uma variável com o modificador input não pode ser modificada dentro de programas mql5, tais variáveis podem ser acessadas somente para leitura. Valores de variáveis de entrada podem ser modificados pelo usuário somente a partir da janela de propriedades do programa. Le variabili esterne vengono sempre reinizializzate immediatamente prima che OnInit() venga chiamato.

O comprimento máximo de um nome de variável input é 63 caracteres. Além disso, para um parâmetro de entrada do tipo string o comprimento do valor máximo (comprimento da string) pode variar de 191 a 253 caracteres (veja a Observação) O comprimento mínimo é de 0 caracteres (nenhum valor especificado).

Exemplo:

//— parâmetros de entrada
input int            MA_Period=13;
input int            MA_Shift=0;
input ENUM_MA_METHOD MA_Method=MODE_SMMA;

Variáveis de entrada determinam a entrada de parâmetros de um programa. Eles estão disponíveis a partir da janela de Propriedades de um programa.

Defina um valor para um parâmetro de entrada

É possível definir uma outra forma de exibir nomes de parâmetros de entrada na aba Parâmetros de Entrada. Para fazer isso, um comentário string é usado, que deve estar localizado após a descrição de um parâmetro de entrada na mesma linha. Assim, nomes mais compreensivos para o usuário podem ser associados aos parâmetros de entrada.

Exemplo:

//— parâmetros de entrada
input int            InpMAPeriod=13;         // Período suavizado
input int            InpMAShift=0;           // Deslocamento da linha horizontal
input ENUM_MA_METHOD InpMAMethod=MODE_SMMA;  // Método de Suavização
Método razoável de exibir parâmetros de entrada

Observação:Arrays e variáveis de tipos complexos não podem agir como variáveis de entrada.

Observação:O comprimento de um comentário string para as variáveis ​​de entrada não pode exceder 63 caracteres.

Observação: Para variáveis input do tipo string a limitação do comprimento do valor (o comprimento da string) é especificada pelas seguintes condições:

  • o valor do parâmetro é representado pela string “parameter_name=parameter_value” (o símbolo ‘=’ é levado em consideração),
  • comprimento máximo de representação de 255 caracteres (total_length_max=255 ou 254 caracteres excluindo ‘=’),
  • comprimento máximo de um nome de parâmetro de string parameter_name_length = 63 caracteres.

Assim, o tamanho máximo da string para um parâmetro de string pode ser calculado pela fórmula:

parameter_value_length=total_length_max-parameter_name_length=254-parameter_name_length

Isso dá um tamanho máximo de string de 191 (parameter_name_length=63) a 253 caracteres (parameter_name_length=1).

Passando Parâmetros Ao Chamar Indicadores Customizados a partir de Programas MQL5 #

Indicadores Customizados são chamados usando a função iCustom(). Após o nome do indicador customizado, os parâmetros devem aparecer em estrita conformidade com a declaração das variáveis de entrada deste indicador customizado. Se os parâmetros indicados são em número menor que as variáveis de entrada declaradas no indicador customizado chamado, os parâmetros faltantes são preenchidos com os valores especificados durante a declaração de variáveis.

Se o indicador customizado usar a função OnCalculate do primeiro tipo (isto é, o indicador é calculado usando o mesmo array dos dados), então um dos valores ENUM_APPLIED_PRICE ou o manuseio de um outro indicador deve ser usado como o último parâmetro ao chamar tal indicador customizado. Todos os parâmetros correspondentes às variáveis de entrada devem ser claramente indicados.

Enumerações como Parâmetros de Entrada

Não apenas enumerações internas fornecidos no MQL5, mas também variáveis definidas pelo usuário podem ser usadas como variáveis de entrada (parâmetros de entrada para programas MQL5). Não apenas enumerações internas fornecidos no MQL5, mas também variáveis definidas pelo usuário podem ser usadas como variáveis de entrada (parâmetros de entrada para programas MQL5).

Exemplo:

#property script_show_inputs
//— dias da semana
enum dayOfWeek 
  {
   S=0,     // Domingo
   M=1,     // Segunda-feira
   T=2,     // Terça-feira
   W=3,     // Quarta-feira
   Th=4,    // Quinta-feira
   Fr=5,    // Sexta-feira,
   St=6,    // Sábado
  };
//— parâmetros de entrada
input dayOfWeek swapday=W;

A fim de permitir a um usuário selecionar um valor necessário da janela de propriedades durante o início da execução de um script, nós usamos o comando de pré-processamento #property script show inputs. Iniciamos o script e podemos escolher um dos valores da enumeração dayOfWeek da lista. Iniciamos o script EnumInInput e vamos para a aba Parâmetros de Entrada. Por default, o valor de swapday (dia de cobrança tripla de swap) é Wednesday (W = 3), mas nós podemos especificar qualquer outro valor, e usar este valor para mudar a operação do programa.

Exemplo de uma enumeração customizada como um parâmetro de entrada

O número de possíveis valores de uma enumeração é limitada. A fim de obter o código de erro, a função GetLastError() deve ser chamada. Nomes mnemônicos de membros da enumeração são usados para valores exibidos na lista. Se um comentário é associado com um nome mnemônico, como mostrado no exemplo, o conteúdo do comentário é usado ao invés do nome mnemônico.

Cada valor da enumeração dayOfWeek tem seu valor de 0 a 6, mas na lista de parâmetros, os comentários especificados para cada valor será exibido. Isso fornece flexibilidade adicional para escrever programas com descrições mais claras dos parâmetros de entrada.

Variáveis com Modificador sinput #

Variáveis com modificador input não apenas permitem definir parâmetros externos ao iniciar programas mas também são necessárias na otimização de estratégias de negociação no Provador de Estratégia. Cada variável de entrada excluindo-se o do tipo string pode ser usada em optimização.

Algumas vezes, é necessário excluir alguns parâmetros externos do programa da área total de passos do Provador de Estratégia. O modificador de memória sinput foi introduzido para tais casos. sinput significa declaração de variável externa estática (sinput = static input). Significa que a declaração seguinte em um código Expert Advisor

sinput       int layers=6;   // Número de camadas

será equivalente a declaração completa

static input int layers=6;   // Número de camadas

A variável declarada com o modificador sinput é um parâmetro de entrada de um programa MQL5. O valor deste parâmetro pode ser modificado ao iniciar o programa. Entretanto, esta variável não pode ser usado na otimização de parâmetros de entrada. Em outras palavras, seu valor não é enumerado ao procurar pelo melhor conjunto ajustado de parâmetros a uma condição específica.

Parâmetro sinput no Provador de Estratégia

O Expert Advisor mostrado acima tem 5 parâmetros externos. “Número de camadas” é declarado para ser sinput e igual a 6. Este parâmetro não pode ser modificado durante um otimização de estratégia de negociação. Podemos especificar o valor necessário para ele ser usado mais adiante. Os campos Iniciar, Passo, Parar não ficam disponíveis para tal variável.

Portanto, usuários não serão capazes de otimizar este parâmetro após especificarmos o modificador sinput para a variável. Em outras palavras, o usuário do terminal não será capaz de definir os valores inicial e final para ele no Provador de Estratégia para enumeração automática na faixa especificada durante uma otimização.

Porém, existe uma exceção a esta regra: variáveis sinput podem variar em tarefas de optimização usando a função ParameterSetRange(). Esta função foi introduzida especificamente para o programa controlar os conjuntos de valores disponíveis para qualquer variável input, incluindo aquelas declaradas como static input (sinput). A função ParameterGetRange() permite receber os valores variáveis ​​de entrada quando a otimização é iniciada (com o manipulador OnTesterInit()) e redefine uma alteração do valor do passo e um intervalo, no qual uma otimização dos valores dos parâmetros serão enumerados.

Desta forma, ao combinando o modificador sinput com duas funções que trabalham com parâmetros de entrada, isso permite criar regras flexíveis para definir intervalos de otimização de parâmetros de entrada que dependem dos valores de outros parâmetros de entrada.

Agrupando Parâmetros de Entrada #

Para facilitar o trabalho com programas MQL5, os parâmetros de entrada podem ser divididos em blocos nomeados usando a palavra-chave group. Isso permite que você separe visualmente alguns parâmetros de outros com base na lógica incorporada neles.

input group           “Nome do grupo”
input int             variable1 = …
input double          variable1 = …
input diuble          variable3= …

Todos os parâmetros de entrada após essa declaração são visualmente agrupados no grupo especificado, facilitando para o usuário do programa MQL5 configurar parâmetros ao executar no gráfico ou no testador de estratégia. A indicação de cada grupo é válida até que apareça uma declaração do grupo:

input group           “Nome do grupo #1”
input int             group1_var1 = …
input double          group1_var2 = …
input double          group1_var3 = …
 
input group           “Nome do grupo #2
input int             group2_var1 = …
input double          group2_var2 = …
input double          group2_var3 = …

Exemplo de EA no qual o bloco de parâmetros de entrada é dividido segundo sua finalidade:

input group           “Signal”
input int             ExtBBPeriod   = 20;       // Bollinger Bands period
input double          ExtBBDeviation= 2.0;      // deviation
input ENUM_TIMEFRAMES ExtSignalTF=PERIOD_M15;   // BB timeframe
 
input group           “Trend”
input int             ExtMAPeriod  = 13;        // Moving Average period
input ENUM_TIMEFRAMES ExtTrendTF=PERIOD_M15;    // MA timeframe
 
input group           “ExitRules”
input bool            ExtUseSL      = true;     // use StopLoss
input int             Ext_SL_Points = 50;       // StopLoss in points
input bool            ExtUseTP      = false;    // use TakeProfit
input int             Ext_TP_Points = 100;      // TakeProfit in points
input bool            ExtUseTS      = true;     // use Trailing Stop
input int             Ext_TS_Points = 30;       // Trailing Stop in points
 
input group           “MoneyManagement”
sinput double         ExtInitialLot = 0.1;      // initial lot value
input bool            ExtUseAutoLot = true;     // automatic lot calculation
 
input group           “Auxiliary”
sinput int            ExtMagicNumber = 123456;  // EA Magic Number
sinput bool           ExtDebugMessage= true;    // print debug messages

Ao iniciar este EA no testador de estratégia, você pode clicar duas vezes no nome do grupo para recolher e expandir o bloco de parâmetros de entrada, além de selecionar todos os seus parâmetros com um clique na caixa de seleção do grupo para otimização.

Agrupando os parâmetros de entrada do EA no testador de estratégia

Veja Também

iCustomEnumeraçõesPropriedades de Programas

Inicialização de Variáveis

Qualquer variável pode ser inicializada durante a definição. Se uma variável não é inicializada explicitamente, o valor armazenado nesta variável pode ser qualquer coisa. Inicialização implícita não é usada.

Variáveis globais e estáticas podem ser inicializadas uma vez por uma constante do tipo correspondente ou uma expressão constante. Variáveis locais podem ser inicializados por qualquer expressão, não apenas por uma constante.

A inicialização de variáveis globais e estáticas é realizada apenas uma vez. A inicialização de variáveis locais é feita toda a vez que se chama as funções correspondentes.

Exemplos:

int    n       = 1;
string s       = “Olá”;
double f[]     = { 0.0, 0.236, 0.382, 0.5, 0.618, 1.0 };
int    a[4][4] = { {1, 1, 1, 1}, {2, 2, 2, 2}, {3, 3, 3, 3}, {4, 4, 4, 4 } };
//— de tetris
int    right[4]={WIDTH_IN_PIXELS+VERT_BORDER,WIDTH_IN_PIXELS+VERT_BORDER,
                 WIDTH_IN_PIXELS+VERT_BORDER,WIDTH_IN_PIXELS+VERT_BORDER};
//— inicialização de todos os campos da estrutura com valores zero
MqlTradeRequest request={0};

A lista de valores dos elementos de um array devem estar entre chaves. Seqüências de inicializações faltantes são consideradas iguais a 0. A seqüência de inicialização deve ter ao menos um valor: este valor é inicializado para o primeiro elemento da estrutura correspondente ou array, elementos faltantes são considerados iguais a zero.

Se o tamanho do array inicializado não é especificado, ele é determinado pelo compilador, baseado no tamanho da seqüência de inicialização. Arrays multi-dimensionais não podem ser inicializados por uma seqüência unidimensional (uma seqüência sem chaves adicionais), exceto para o caso quando apenas um elemento inicializante é especificado (zero, como regra).

Arrays (incluindo aqueles declarados no nível local) podem ser inicializados apenas por constantes.

Exemplos:

struct str3
  {
   int               low_part;
   int               high_part;
  };
struct str10
  {
   str3              s3;
   double            d1[10];
   int               i3;
  };
void OnStart()
  {
   str10 s10_1={{1,0},{1.0,2.1,3.2,4.4,5.3,6.1,7.8,8.7,9.2,10.0},100};
   str10 s10_2={{1,0},{0},100};
   str10 s10_3={{1,0},{1.0}};
//—
   Print(“1.  s10_1.d1[5] = “,s10_1.d1[5]);
   Print(“2.  s10_2.d1[5] = “,s10_2.d1[5]);
   Print(“3.  s10_3.d1[5] = “,s10_3.d1[5]);
   Print(“4.  s10_3.d1[0] = “,s10_3.d1[0]);
  }

 
Para variável de tipo estrutura, inicialização parcial é permitida, bem como para arrays estáticos. Pode-se inicializar um ou mais primeiros elementos de uma estrutura ou array, os demais elementos serão inicializados com zeros neste caso.

Também Veja

Tipos de DadosEncapsulamento e Tipo de ExtensibilidadeVisibilidade Escopo e Tempo de Vida de VariáveisCriando e Excluindo Objetos

Visibilidade Escopo e Tempo de Vida de Variáveis

Existem dois tipos básicos de escopo: escopo local e escopo global.

Uma variável declarada fora de todas as funções está localizada no escopo global. O acesso a tais variáveis pode ser feito de qualquer lugar no programa. Estas variáveis são localizadas no conjunto (pool) global de memória, assim seus tempos de vida coincidem com o tempo de vida do programa.

Uma variável declarada dentro de um bloco (parte do código entre chaves) pertence ao escopo local. Tal variável não é visível (e portanto não é disponível) fora do bloco no qual está definida. O caso mais comum de declaração local é uma variável declarada dentro de uma função. Uma variável declarada localmente está localizada na pilha (stack), e o tempo de vida de tal variável é igual ao tempo de vida da função.

Já que o escopo de uma variável local é o bloco em que ela está declarada, é possível declarar variáveis com o mesmo nome que variáveis declaradas em outros blocos, mesmo aquelas declaradas em níveis mais acima, até o nível global.

Exemplo:

void CalculateLWMA(int rates_total,int prev_calculated,int begin,const double &price[])
  {
   int        i,limit;
   static int weightsum=0;
   double     sum=0;
//—
   if(prev_calculated==0)
     {
      limit=MA_Period+begin;
      //— define valor vazio para primeiras barras limite
      for(i=0; i<limit; i++) LineBuffer[i]=0.0;
      //— calcula primeiro valor visível
      double firstValue=0;
      for(int i=begin; i<limit; i++)
        {
         int k=i-begin+1;
         weightsum+=k;
         firstValue+=k*price[i];
        }
      firstValue/=(double)weightsum;
      LineBuffer[limit-1]=firstValue;
     }
   else
     {
      limit=prev_calculated-1;
     }
 
   for(i=limit;i<rates_total;i++)
     {
      sum=0;
      for(int j=0; j<MA_Period; j++) sum+=(MA_Period-j)*price[i-j];
      LineBuffer[i]=sum/weightsum;
     }
//—
  }

Preste atenção na variável i, declarada na linha

      for(int i=begin; i<limit; i++)
        {
         int k=i-begin+1;
         weightsum+=k;
         firstValue+=k*price[i];
        }

Seu escopo é somente o ciclo do for; fora deste ciclo existe uma outra variável com o mesmo nome, declarada no começo da função. Além disso, a variável k está declarada no corpo do loop, seu escopo é o corpo do loop.

Variáveis locais podem ser declarados com o especificador de acesso static. Neste caso, o compilador tem uma variável no conjunto (pool) global de memória. Portanto, o tempo de vida de uma variável estática é igual ao tempo de vida do programa. Aqui o escopo de tal variável é limitado ao bloco no qual ela está declarada.

Também Veja

Data TypesEncapsulation and Extensibility of Types,Initialization of VariablesCreating and Deleting Objects

Criação e Exclusão de Objetos

Após um programa MQL5 ser carregado para execução, memória é alocada para cada variável de acordo com seu tipo. De acordo com o nível de acesso, todas as variáveis são dividas em dois tipos – variáveis globais e variáveis locais. De acordo com a classe de memória, elas pode ser parâmetros de entrada de um programa MQL5, estáticas (static) e automáticas. Se necessário, cada variável é inicializada por um valor correspondente. Após ser usada, uma variável é desinicializada e a memória usada por ela é retornada ao sistema executável MQL5.

Inicialização e Desinicialização de Variáveis Globais

Variáveis globais são inicializados automaticamente logo após um programa mql5 ser carregado e antes de qualquer função ser chamada. Durante a inicialização valores iniciais são atribuídos a variáveis de tipos simples e um construtor (se existir algum) é chamado para objetos. Variáveis de entrada são sempre declarados em um nível global, e são inicializados por valores definidos pelo usuário na janela de diálogo durante o começo do programa.

Apesar do fato de que variáveis estáticas serem geralmente declaradas em um nível local, a memória para estas variáveis é pré-alocada, e a inicialização é realizada logo após um programa ser carregado; o mesmo para variáveis globais.

A ordem de inicialização corresponde a ordem de declaração da variável no programa. A desinicialização é realizada em ordem inversa. Este regra é verdadeira apenas para as variáveis que não foram criadas pelo operador new. Tais variáveis são criadas e inicializadas automaticamente logo após carregamento, e são desinicializadas depois do descargamento do programa.

Inicialização e Desinicialização de Variáveis Locais

Se uma variável declarada em um nível local não é estática, memória é alocada automaticamente para tal variável. Variáveis locais, bem como variáveis globais, são inicializados automaticamente no momento que a execução do programa encontra a declaração de uma variável local. Assim, a ordem de inicialização corresponde à ordem de declaração.

Variáveis locais são desinicializados no final do bloco do programa, no qual elas foram declaradas, e na ordem oposta de suas declarações. Um bloco de programa é um operador de composição que pode ser uma parte de um operador de seleção switch, operador de ciclo (forwhiledo-while), um corpo de função ou uma parte do operador if-else.

Variáveis locais são inicializados somente no momento em que a execução do programa encontra a declaração da variável. Se durante a execução do programa o bloco, no qual a variável está declarada, não for executada, tal variável não é inicializada.

Inicialização e Desinicialização de Objetos Colocados

Um caso especial é aquele com ponteiros de objeto, porque a declaração de um ponteiro não implica em inicialização de objetos correspondentes. Objetos colocados dinamicamente são inicializados somente no momento em que a instância da classe é criada pelo operador new. Inicialização de objetos pressupõe chamada de um construtor da classe correspondente. Se não existir construtor correspondente na classe, seus membros de tipo simples não serão automaticamente inicializados; membros de tipos stringarray dinâmico e objeto complexo serão automaticamente inicializados.

Ponteiros podem ser declarados em nível local ou global; e eles podem ser inicializados pelo valor vazio de NULL ou pelo valor do ponteiro do mesmo ou herdado tipo. Se o operador new é chamado para um ponteiro declarado em um nível local, o operador delete para este ponteiro deve ser chamado antes de sair do nível. Caso contrário, o ponteiro estará perdido e a exclusão explícita do objeto falhará.

Todos os objetos criados pela expressão object_pointer=new Class_name, devem ser então excluídos pelo operador delete(object_pointer). Se por algum razão tal variável não for excluída por meio do operador delete quando o programa estiver concluído, a correspondente entrada aparecerá no diário “Experts”. Pode-se declarar várias variáveis e atribuir um ponteiro de um objeto para todas elas.

Se um objeto criado dinamicamente tiver um construtor, este construtor será chamado no momento da execução do operador new. Se um objeto tiver um destrutor, ele será chamado durante a execução do operador delete.

Assim objetos colocados dinamicamente são criados somente no momento em que são criados pelo operador new, e são certamente excluídos tanto pelo operador delete ou automaticamente pelo sistema de execução do MQL5 durante a descarga do programa. A ordem de declaração de ponteiros criados dinamicamente não influenciam na ordem de suas inicializações. A ordem de inicialização e desinicialização é totalmente controlada pelo programador.

Características Transitórias de Variáveis

A principal informação sobre a ordem de criação, exclusão, sobre chamadas de construtores e destrutores é dada na tabela a baixo.

Variável automática globalVariável automática localObjeto criado dinamicamente
Inicializaçãologo após um programa MQL5 ser carregadoquando a linha de código onde ela está declarada é alcançada durante execuçãona execução do operador new
Ordem de inicializaçãona ordem da declaraçãona ordem da declaraçãoindependente da ordem de declaração
Desinicializaçãoantes de um programa MQL5 ser descarregadoquando a execução sai do bloco da declaraçãoquando o operador delete é executado ou antes de um programa mql5 ser descarregado
Ordem de desinicializaçãona ordem oposta da ordem de inicializaçãona ordem oposta da ordem de inicializaçãoindependente da ordem de inicialização
Chamada de construtorno carregamento do programa MQL5na inicializaçãona execução do operador new
Chamada de destrutorna descarga do programa MQL5quando sair do bloco onde a variável foi inicializadana execução do operador delete
Logs de Erromensagem de log no diário “Experts” sobre a tentativa de excluir um objeto criado automaticamentemensagem de log no diário “Experts” sobre a tentativa de excluir um objeto criado automaticamentemensagem de logo no diário “Experts” sobre objetos criados dinamicamente não excluídos na descarga de um programa mql5

Também Veja

Tipos de DadosEncapsulamento e Tipos de ExtensibilidadeInicialização de VariáveisVisibilidade Escope e Tempo de Vida de Variáveis

Funções

Toda tarefa pode ser dividida em sub-tarefas, cada qual podendo ser representada tanto na forma de código, como ser divida em sub-tarefas ainda menores. Este método é chamado de refinamento passo a passo.. Funções são usadas para escrever o código das sub-tarefas a serem resolvidas. O código que descreve o que uma função faz é chamado de definição de função:

function_header
  {
  instruções
  }

Tudo que está antes da primeira chave é o cabeçalho (header) da definição de função, e o que está entre as chaves é o corpo (body) da definição de função. O cabeçalho de função (function header) inclui a descrição do tipo de valor de retorno, o nome (identificador) e os parâmetros formais. O número de parâmetros passados para a função é limitado e não pode exceder 64.

A função pode ser chamada de outras partes do programa quantas vezes forem necessárias. Na verdade, o tipo de retorno, o identificador da função e os tipos de parâmetros constituem o protótipo da função.

Protótipo de função é a declaração da função, mas não a sua definição. Devido a declaração explícita do tipo de retorno e da lista dos tipos de argumentos, a verificação estrita de tipo e a implícita conversão de tipo (typecasting) são possíveis durante as chamadas de função. Muito freqüentemente declarações de funções são usadas em classes para melhorar a legibilidade do código.

As definições de função devem corresponder exatamente à sua declaração. Cada função declarada deve ser definida.

Exemplo:

double                       // tipo do valor de retorno
linfunc (double a, double b) // nome da função e lista de parâmetros
  {
                             // operador composto
   return (a + b);           // valor do retorno
  }

O operador return pode retornar o valor de uma expressão localizada neste operador. Se necessário, o valor da expressão é convertido para o tipo do resultado da função. Pode ser retornado: tipos simplesestruturas simplesponteiros de objetos. Com o operador return não se pode retornar arrays, objetos de classe, variáveis de tipo estrutura composta.

Uma função que não retorna nenhum valor deve ser descrita como do tipo void.

Exemplo:

void errmesg(string s)
  {
   Print(“erro: “+s);
  }

Parâmetros passado para a função podem ter valores default (padrão), que são definidos por constantes daquele tipo.

Exemplo:

int somefunc(double a,
             double d=0.0001,
             int n=5,
             bool b=true,
             string s=”string transmitida”)
  {
   Print(“Parâmetro obrigatório a = “,a);
   Print(“Transmitir os seguintes parâmetros: d = “,d,” n = “,n,” b = “,b,” s = “,s);
   return(0);
  }

Se algum dos parâmetros tem um valor default, todos os parâmetros subseqüentes devem também ter valores default.

Exemplo de declaração incorreta:

int somefunc(double a,
             double d=0.0001,    // valor padrão declarado 0.0001
             int n,              // não é determinado o valor padrão!
             bool b,             // não é determinado o valor padrão!
             string s=”string transmitida”)
  {                                          
  }           

Também Veja

Sobrecarga (Overload)Funções VirtuaisPolimorfismo

Chamada de Função

Se um nome, que não foi descrito antes, aparece em uma expressão e é seguido pelo parêntesis de abertura, ele é considerado contextualmente como o nome de uma função.

function_name (x1, x2,…, xn)

Argumentos (parâmetros formais) são passados por valor, isto é, cada expressão x1,.., xn é calculada, e os valores são passados para a função. A ordem de cálculo das expressões e a ordem de carregamento dos valores não são garantidos. Durante a execução, o sistema verifica o número e o tipo dos argumentos passados para a função. Tal forma de abordar uma função é chamada de uma chamada de valor.

Chamada de função é uma expressão, cujo valor é o valor retornado pela função. O tipo da função descrito acima deve corresponder com o tipo do valor de retorno. Uma função pode ser declarada ou descrita em qualquer parte do programa no escopo global, isto é, fora de outras funções. Uma função não pode ser declarada ou descrita dentro de outra função.

Exemplos:

int start()
  {
   double some_array[4]={0.3, 1.4, 2.5, 3.6};
   double a=linfunc(some_array, 10.5, 8);
   //…
  }
double linfunc(double x[], double a, double b)
  {
   return (a*x[0] + b);
  }

Na chamada de uma função com parâmetros default, a lista de parâmetros a serem passados pode ser limitada, mas não antes do primeiro parâmetro default.

Exemplos:

void somefunc(double init,
              double sec=0.0001, //define valores default
              int level=10);  
//…
somefunc();                      // Chamada errada. O primeiro parâmetro deve estar presente.
somefunc(3.14);                  // Chamada correta
somefunc(3.14,0.0002);           // Chamada correta
somefunc(3.14,0.0002,10);        // Chamada correta

Ao chamar uma função, não se pode pular parâmetros, mesmo aqueles que têm valores default:

somefunc(3.14, , 10);           // Chamada errada -> o segundo parâmetros foi pulado.

Também Veja

Sobrecarga (Overload)Funções VirtuaisPolimorfismo

Passando Parâmetros

Existem pois métodos pelo qual a linguagem de máquina pode passar argumentos para um subprograma (função). O primeiro método é enviar um parâmetro por valor. Este método copia o valor do argumento para um parâmetro formal de função. Portanto, quaisquer mudanças neste parâmetro ocorridas dentro da função não têm influência no correspondente argumento usado na chamada da função.

//+——————————————————————+
//| Passando parâmetros por valor                                    |
//+——————————————————————+
double FirstMethod(int i,int j)
  {
   double res;
//—
   i*=2;
   j/=2;
   res=i+j;
//—
   return(res);
  }
//+——————————————————————+
//| Programa Script da função start (iniciar)                        |
//+——————————————————————+
void OnStart()
  {
//—
   int a=14,b=8;
   Print(“a e b antes chamada:”,a,” “,b);
   double d=FirstMethod(a,b);
   Print(“a e b após chamada:”,a,” “,b);
  }
//— Resultado da execução do script
//  a e b antes chamada: 14 8
//  a e b após chamada: 14 8

O segundo método é passar por referência. Neste caso, a referência para um parâmetro (não seu valor) é passada para um parâmetro de função. Dentro da função, ele é usado para referenciar o verdadeiro parâmetro especificado na chamada. Isso significa que mudanças no parâmetro afetarão o argumento usado na chamada da função.

//+——————————————————————+
//| Passando parâmetros por referência                               |
//+——————————————————————+
double SecondMethod(int &i,int &j)
  {
   double res;
//—
   i*=2;
   j/=2;
   res=i+j;
//—
   return(res);
  }
//+——————————————————————+
//| Programa Script da função start (iniciar)                        |
//+——————————————————————+
void OnStart()
  {
//—
   int a=14,b=8;
   Print(“a e b antes chamada:”,a,” “,b);
   double d=SecondMethod(a,b);
   Print(“a e b após chamada:”,a,” “,b);
  }
//+——————————————————————+
//— resultado da execução do script
//  a e b antes chamada: 14 8
//  a e b após chamada: 28 4

MQL5 usa ambos os métodos, com uma exceção, arrays, variáveis tipo estrutura e objetos de classe são sempre passados por referência. A fim de evitar modificações nos parâmetros reais (argumentos passados na chamada da função) use o especificador de acesso const. Ao se tentar modificar o conteúdo de uma variável declarada com o especificador const, o compilador gerará um erro.

Observação

Deve se notar que os parâmetros são passados para uma função em ordem inversa, ou seja, o último parâmetro é calculado e passado primeiro, depois o último mas apenas um, etc. O último parâmetro calculado e passado é aquele que está em primeiro lugar depois da abertura dos parênteses.

Exemplo:

void OnStart()
  {
//—
   int a[]={0,1,2};
   int i=0;
 
   func(a[i],a[i++],”First call (i = “+string(i)+”)”);
   func(a[i++],a[i],”Second call (i = “+string(i)+”)”);
// Result:
// First call (i = 0) : par1 = 1     par2 = 0
// Second call (i = 1) : par1 = 1     par2 = 1
 
  }
//+——————————————————————+
//|                                                                  |
//+——————————————————————+
void func(int par1,int par2,string comment)
  {
   Print(comment,”: par1 = “,par1,”    par2 = “,par2);
  }

Na primeira chamada (ver exemplo acima) a variável i é usada pela primeira vez na concatenação de strings:

  “Primeira chamada (i = “+string(i)+”)”

Aqui o valor não muda. Então a variável i é usada no cálculo do elemento do array a[i++], ou seja, quando o elemento do array com índice i é acessado, a variável i é incrementada. E só depois disso o primeiro parâmetro com valor alterado da variável i é calculado.

Na segunda chamada o mesmo valor de i (calculado sobre a primeira fase da função chamada) é utilizado no cálculo de todos os três parâmetros. Somente depois de os primeiros parâmetros serem calculados, a variável i é alterada novamente.

Também Veja

Escopo de visibilidade e tempo de vida das variáveis, SobrecargaFunções VirtuaisPolimorfismo

Sobrecarga de Função

Geralmente, o nome da função tende a refletir seu propósito principal. Como regra, programas legíveis contém vários identificadores bem selecionados. Às vezes, funções diferentes são usados para os mesmos propósitos. Vamos considerar, por exemplo, uma função que calcula o valor médio de um array de números de dupla precisão e a mesma função, mas operando com um array de inteiros. Ambas são convenientemente chamadas de AverageFromArray:

//+——————————————————————+
//| O cálculo de média de um array de tipo double                    |
//+——————————————————————+
double AverageFromArray(const double & array[],int size)
  {
   if(size<=0) return 0.0;
   double sum=0.0;
   double aver;
//—
   for(int i=0;i<size;i++)
     {
      sum+=array[i];    // Soma para o tipo double
     }
   aver=sum/size;       // Apenas divide a soma pelo número
//—
   Print(“Cálculo da média para um array do tipo double”);
   return aver;
  }
//+——————————————————————+
//| O cálculo de média para um array de tipo int                     |
//+——————————————————————+
double AverageFromArray(const int & array[],int size)
  {
   if(size<=0) return 0.0;
   double aver=0.0;
   int sum=0;
//—
   for(int i=0;i<size;i++)
     {
      sum+=array[i];     // Soma para o tipo int
     }
   aver=(double)sum/size;// Fornece uma quantidade de tipo double, e divide
//—
   Print(“Cálculo da média para um array do tipo int”);
   return aver;
  }

Cada função contém a saída da mensagem via a função Print();

   Print(“Cálculo da média para um array do tipo int”);

O compilador seleciona a função necessária de acordo com os tipos dos argumentos e suas quantidades. A regra, que define a escolha a ser feita, é chamada de algoritmo de correspondência de assinatura. Um assinatura é uma lista de tipos usados na declaração da função.

Exemplo:

//+——————————————————————+
//| Programa Script da função start (iniciar)                        |
//+——————————————————————+
void OnStart()
  {
//—
   int    a[5]={1,2,3,4,5};
   double b[5]={1.1,2.2,3.3,4.4,5.5};
   double int_aver=AverageFromArray(a,5);
   double double_aver=AverageFromArray(b,5);
   Print(“int_aver = “,int_aver,”   double_aver = “,double_aver);
  }
//— Resultado do script
// Calcula a média para um array de tipo int
// Calcula a média para um array de tipo double
// int_aver= 3.00000000    double_aver= 3.30000000

Sobrecarga de função é um processo de criar várias funções com o mesmo nome, mas com diferentes parâmetros. Isso significa que em variantes sobrecarregadas de uma função, o número de argumentos e/ou seus tipos devem ser diferentes. Um variante de função específica é selecionada baseada na correspondência entre lista de argumentos ao chamar a função com a lista de parâmetros na declaração da função.

Quando uma função sobrecarregada é chamada, o compilador deve ter uma algoritmo para selecionar a função apropriada. O algoritmo que realiza esta escolha depende da conversão de quais tipos estiverem presentes. A melhor correspondência deve ser única. Uma função sobrecarregada deve ser a melhor correspondência dentre todas as outras variantes para ao menos um argumento. Ao mesmo tempo, ela não deve ser pior que as outras variantes para todos os outros argumentos.

Abaixo está um algoritmo de correspondência para cada argumento.

Algoritmo de Escolha de uma Função Sobrecarregada

  1. Use estrita correspondência (se possível).
  2. Tente incremento de tipo padrão.
  3. Tente conversão de tipo padrão.

O incremento de tipo padrão é melhor que outras conversões padrão. Incremento é a conversão de float para double, de bool, char, short ou enum para int. Conversão de tipo de arrays de tipos inteiros similares também pertencem a conversão de tipo. Tipos similares são: bool, char, uchar, já que todos os três tipo são inteiros de único bytes, inteiros de duplo byte short e ushort; inteiros de 4 bytes int, uint, e color; long, ulong e datetime.

Claro que a estrita correspondência é a melhor. Para alcançar tal consistência conversão de tipo (typecasting) pode ser usada. O compilador não pode lidar com situações ambíguas. Portanto você não deve confiar em diferenças sutis de tipos e conversões implícitas que tornam a função sobrecarregada não clara.

No caso de dúvida, use conversão explícita para assegurar estrita adequação.

Exemplos de funções sobrecarregadas no MQL5 podem ser vistas no exemplo de funções ArrayInitialize().

Regras de sobrecarga de função para sobrecarga de métodos de classe.

A sobrecarga de funções de sistema é permitida, mas deve-se observar que o compilador é capaz de selecionar com precisão a função necessária. Por exemplo, podemos sobrecarregar o sistema da função MathMax() de 4 maneiras diferentes, mas apenas duas variantes são corretas.

Exemplo:

// 1. sobrecarga é permitida – função difere da função embutida MathMax() em número de parâmetros 
double MathMax(double a,double b,double c);
 
// 2. sobrecarga não é permitida!
// número de parâmetros é diferente, mas o último tem um valor default
// isso leva ao ocultamento da função de sistema ao chamar-lo, o que é inaceitável
double MathMax(double a,double b,double c=DBL_MIN);
 
// 3. sobrecarga é permitida – sobrecarga normal para tipos de parâmetros ‘a’ e ‘b’
double MathMax(int a,int b);
 
// 4. sobrecarga não é permitida!
// o número e tipos de parâmetros são os mesmos que no original double MathMax (double a,double b)
int MathMax(double a,double b);

Também Veja

Sobrecarga (Overload)Funções VirtuaisPolimorfismo

Sobrecarga de Operação

Para facilitar a leitura e escrita de código, a sobrecarga de algumas operações é permitida. O operador de sobrecarga é escrito usando a palavra-chave operator. Os seguintes operadores podem ser sobrecarregados:

  • binário +,-,/,*,%,<<,>>,==,!=,<,>,<=,>=,=,+=,-=,/=,*=,%=,&=,|=,^=,<<=,>>=,&&,||,&,|,^
  • unário +,-,++,–,!,~
  • operador de atribuição =
  • operador de indexação []

Sobrecarga de operação permite o uso da notação de operação (escrita na forma de expressões simples) para objetos complexos – estruturas e classes. Escrevendo expressões usando operações de sobrecarga simplifica a visualização do código fonte, porque uma implementação mais complexa fica escondida.

Por exemplo, considere números complexos, que consistem de partes real e imaginária. Eles são amplamente utilizados na matemática. A linguagem MQL5 não tem um tipo de dado que represente números complexos, mas é possível criar um novo tipo de dado na forma de uma estrutura ou classe. Declare a estrutura complexa e defina quatro métodos que implementam as quatro operações aritméticas:

//+——————————————————————+
//| Uma estrutura para operações com números complexos               |
//+——————————————————————+
struct complex
  {
   double            re; // Parte real
   double            im; // Parte imaginário
   //— Construtores
                     complex():re(0.0),im(0.0) {  }
                     complex(const double r):re(r),im(0.0) {  }
                     complex(const double r,const double i):re(r),im(i) {  }
                     complex(const complex &o):re(o.re),im(o.im) { }
   //— Operações Aritméticas
   complex           Add(const complex &l,const complex &r) const;  // Adição
   complex           Sub(const complex &l,const complex &r) const;  // Subtração
   complex           Mul(const complex &l,const complex &r) const;  // Multiplicação
   complex           Div(const complex &l,const complex &r) const;  // Divisão
  };

Agora, em nosso código nós podemos declarar variáveis representando números complexos, e trabalhar com eles.

Por exemplo:

void OnStart()
  {
//— Declara e inicialize variáveis de um tipo complexo
   complex a(2,4),b(-4,-2);
   PrintFormat(“a=%.2f+i*%.2f,   b=%.2f+i*%.2f”,a.re,a.im,b.re,b.im);
//— Soma dois números
   complex z;
   z=a.Add(a,b);
   PrintFormat(“a+b=%.2f+i*%.2f”,z.re,z.im);
//— Multiplica dois números
   z=a.Mul(a,b);
   PrintFormat(“a*b=%.2f+i*%.2f”,z.re,z.im);
//— Dividir dois números
   z=a.Div(a,b);
   PrintFormat(“a/b=%.2f+i*%.2f”,z.re,z.im);
//—
  }

Mas seria mais conveniente usar os operadores usuais “+”, “-“, “*” e “/” para operações aritméticas comuns com números complexos.

A palavra-chave operator é usado para definir uma função membro que realiza conversão de tipo. Operações unárias e binárias para variáveis de objeto de classe podem ser sobrecarregadas como funções membro não estáticas. Elas implicitamente agem nos objetos de classe.

A maioria das operações binárias podem ser sobrecarregadas como funções regulares que tomam uma variável de classe e/ou um ponteiro de objeto desta classe como argumento. Para o nosso tipo complexo, a sobrecarga na declaração se parecerá como:

   //— Operadores
   complex operator+(const complex &r) const { return(Add(this,r)); }
   complex operator-(const complex &r) const { return(Sub(this,r)); }
   complex operator*(const complex &r) const { return(Mul(this,r)); }
   complex operator/(const complex &r) const { return(Div(this,r)); }

O exemplo completo do script:

//+——————————————————————+
//| Programa Script da função start (iniciar)                        |
//+——————————————————————+
void OnStart()
  {
//— Declara e inicialize variáveis de um tipo complexo
   complex a(2,4),b(-4,-2);
   PrintFormat(“a=%.2f+i*%.2f,   b=%.2f+i*%.2f”,a.re,a.im,b.re,b.im);
   //a.re=5;
   //a.im=1;
   //b.re=-1;
   //b.im=-5;
//— Soma dois números
   complex z=a+b;
   PrintFormat(“a+b=%.2f+i*%.2f”,z.re,z.im);
//— Multiplica dois números
 
   z=a*b;
   PrintFormat(“a*b=%.2f+i*%.2f”,z.re,z.im);
//— Dividir dois números
   z=a/b;
   PrintFormat(“a/b=%.2f+i*%.2f”,z.re,z.im);
//—
  }
//+——————————————————————+
//| Uma estrutura para operações com números complexos               |
//+——————————————————————+
struct complex
  {
   double            re; // Parte real
   double            im; // Parte imaginário
   //— Construtores
                     complex():re(0.0),im(0.0) {  }
                     complex(const double r):re(r),im(0.0) {  }
                     complex(const double r,const double i):re(r),im(i) {  }
                     complex(const complex &o):re(o.re),im(o.im) { }
   //— Operações Aritméticas
   complex           Add(const complex &l,const complex &r) const;  // Adição
   complex           Sub(const complex &l,const complex &r) const;  // Subtração
   complex           Mul(const complex &l,const complex &r) const;  // Multiplicação
   complex           Div(const complex &l,const complex &r) const;  // Divisão
   //— Operadores binárias
   complex operator+(const complex &r) const { return(Add(this,r)); }
   complex operator-(const complex &r) const { return(Sub(this,r)); }
   complex operator*(const complex &r) const { return(Mul(this,r)); }
   complex operator/(const complex &r) const { return(Div(this,r)); }
  };
//+——————————————————————+
//| Adição                                                           |
//+——————————————————————+
complex complex::Add(const complex &l,const complex &r) const
  {
   complex res;
//—
   res.re=l.re+r.re;
   res.im=l.im+r.im;
//— Resultado
   return res;
  }
//+——————————————————————+
//| Subtração                                                        |
//+——————————————————————+
complex complex::Sub(const complex &l,const complex &r) const
  {
   complex res;
//—
   res.re=l.re-r.re;
   res.im=l.im-r.im;
//— Resultado
   return res;
  }
//+——————————————————————+
//| Multiplicação                                                    |
//+——————————————————————+
complex complex::Mul(const complex &l,const complex &r) const
  {
   complex res;
//—
   res.re=l.re*r.re-l.im*r.im;
   res.im=l.re*r.im+l.im*r.re;
//— Resultado
   return res;
  }
//+——————————————————————+
//| Divisão                                                          |
//+——————————————————————+
complex complex::Div(const complex &l,const complex &r) const
  {
//— Numero complexo vazio
   complex res(EMPTY_VALUE,EMPTY_VALUE);
//— Verificar se é zero
   if(r.re==0 && r.im==0)
     {
      Print(__FUNCTION__+”: número é zero”);
      return(res);
     }
//— Variáveis auxiliares
   double e;
   double f;
//— Selecionando a variante de cálculo
   if(MathAbs(r.im)<MathAbs(r.re))
     {
      e = r.im/r.re;
      f = r.re+r.im*e;
      res.re=(l.re+l.im*e)/f;
      res.im=(l.im-l.re*e)/f;
     }
   else
     {
      e = r.re/r.im;
      f = r.im+r.re*e;
      res.re=(l.im+l.re*e)/f;
      res.im=(-l.re+l.im*e)/f;
     }
//— Resultado
   return res;
  }

A maioria das operações unárias para classes podem ser sobrecarregadas como funções comuns que aceitam um único argumento de objeto de classe ou ponteiro dele. Adicione sobrecarga de operações unárias “-” e “!”.

//+——————————————————————+
//| Uma estrutura para operações com números complexos               |
//+——————————————————————+
struct complex
  {
   double            re;       // Parte real
   double            im;       // Parte imaginário

   //— Operadores unários
   complex operator-()  const; // Unary minus
   bool    operator!()  const; // Negação
  };

//+——————————————————————+
//| Sobrecarregar operador de “menos unário”                         |
//+——————————————————————+
complex complex::operator-() const
  {
   complex res;
//—
   res.re=-re;
   res.im=-im;
//— Resultado
   return res;
  }
//+——————————————————————+
//| Sobrecarregar operador de “negação lógica”                       |
//+——————————————————————+
bool complex::operator!() const
  {
//— São as partes real e imaginária do número complexo igual a zero?
   return (re!=0 && im!=0);
  }

Agora nós podemos verificar se valor de um número complexo é zero e obter um valor negativo:

//+——————————————————————+
//| Programa Script da função start (iniciar)                        |
//+——————————————————————+
void OnStart()
  {
//— Declara e inicialize variáveis de um tipo complexo
   complex a(2,4),b(-4,-2);
   PrintFormat(“a=%.2f+i*%.2f,   b=%.2f+i*%.2f”,a.re,a.im,b.re,b.im);
//— Dividir dois números
   complex z=a/b;
   PrintFormat(“a/b=%.2f+i*%.2f”,z.re,z.im);
//— Um número complexo é igual a zero por padrão (no construtor padrão re==0 e im==0
   complex zero;
   Print(“!zero=”,!zero);
//— Atribuir um valor negativo
   zero=-z;
   PrintFormat(“z=%.2f+i*%.2f,  zero=%.2f+i*%.2f”,z.re,z.im, zero.re,zero.im);
   PrintFormat(“-zero=%.2f+i*%.2f”,-zero.re,-zero.im);
//— Verificar se é zero mais uma vez  
   Print(“!zero=”,!zero);
//—
  }

Note que nós não tivemos que sobrecarregar o operador de atribuição “=”, já que estruturas de tipos simples pode ser diretamente copiadas uma no outra. Assim, nós agora podemos escrever um código para cálculos envolvendo números complexos de maneira usual.

Sobrecarga de operador de indexação permite obter os valores dos arrays fechados em um objeto, de uma maneira simples e familiar, e isso também contribui para uma melhor legibilidade do código fonte. Por exemplo, nós precisamos fornecer acesso a um símbolo dentro de uma string em uma posição específica. Uma string em MQL5 é um tipo string separado, que não é um array de símbolos, mas com a ajuda de uma operação de indexação sobrecarregada podemos fornecer um trabalho simples e transparente na classe CString gerada:

//+———————————————————————-+
//| Uma classe para acessar símbolos em string como na array de símbolos |
//+———————————————————————-+
class CString
  {
   string            m_string;
  
public:
                     CString(string str=NULL):m_string(str) { }
   ushort operator[] (int x) { return(StringGetCharacter(m_string,x)); }
  };
//+——————————————————————+
//| Programa Script da função start (iniciar)                        |
//+——————————————————————+
void OnStart()  
  {
//— Um array para receber os símbolos a partir de uma string
   int     x[]={ 19,4,18,19,27,14,15,4,17,0,19,14,17,27,26,28,27,5,14,
                 17,27,2,11,0,18,18,27,29,30,19,17,8,13,6 };
   CString str(“abcdefghijklmnopqrstuvwxyz[ ]CS”);
   string  res;
//— Fazer um frase usando símbolos da variável str
   for(int i=0,n=ArraySize(x);i<n;i++)
     {
      res+=ShortToString(str[x[i]]);
     }
//— Mostrar o resultado
   Print(res);
  }

Um outro exemplo de sobrecarga do operador de indexação são operações com matrizes. A matriz representa um array dinâmico de duas dimensões, o tamanho do array não é definido com antecedência. Portanto, você não pode declarar um array da forma array[][] sem especificar o tamanho da segunda dimensão, e então passar este array como um parâmetro. Uma possível solução é uma classe especial CMatrix, que contém um array de objetos de classe CRow.

//+——————————————————————+
//| Programa Script da função start (iniciar)                        |
//+——————————————————————+
void OnStart()
  {
//— Operações de adição e multiplicação de matrizes
   CMatrix A(3),B(3),C();
//— Preparar um array para as linhas
   double a1[3]={1,2,3}, a2[3]={2,3,1}, a3[3]={3,1,2};
   double b1[3]={3,2,1}, b2[3]={1,3,2}, b3[3]={2,1,3};
//— Preencher as matrizes
   A[0]=a1; A[1]=a2; A[2]=a3;
   B[0]=b1; B[1]=b2; B[2]=b3;
//— Saída de matrizes no log Experts
   Print(“—- Os elementos da matriz A”);
   Print(A.String());
   Print(“—- Os elementos da matriz B”);
   Print(B.String());
 
//— Adição de matrizes
   Print(“—- Adição das matrizes A e B”);
   C=A+B;
//— Saída da representação da string formatada
   Print(C.String());
 
//— Multiplicação de matrizes
   Print(“—- Multiplicação das matrizes A e B”);
   C=A*B;
   Print(C.String());
 
//— Agora mostraremos como obter valores no estilo dos arrays dinâmicos matrix[i][j]
   Print(“Saída de valores da matriz C elemento a elemento”);
//— Atravessar as linhas da matriz – objetos CRow – num loop
   for(int i=0;i<3;i++)
     {
      string com=”| “;
      //— Formar linhas a partir da matriz para o valor
      for(int j=0;j<3;j++)
        {
         //— Obter o elemento da matriz pelo número de linha e coluna
         double element=C[i][j];// [i] – Acesso para CRow no array m_rows[] ,
                                // [j] – Operador sobrecarregado da indexação em CRow
         com=com+StringFormat(“a(%d,%d)=%G ; “,i,j,element);
        }
      com+=”|”;
      //— Saída dos valores da linha
      Print(com);
     }
  }
//+——————————————————————+
//| Class “Row”                                                      |
//+——————————————————————+
class CRow
  {
private:
   double            m_array[];
public:
   //— Construtores e um destrutor
                     CRow(void)          { ArrayResize(m_array,0);    }
                     CRow(const CRow &r) { this=r;                    }
                     CRow(const double &array[]);
                    ~CRow(void){};
   //— Número de elementos na linha
   int               Size(void) const    { return(ArraySize(m_array));}
   //— Retorna uma string com valores
   string            String(void) const;
   //— Operador de indexação
   double            operator[](int i) const  { return(m_array[i]);   }
   //— Operadores de atribuição
   void              operator=(const double  &array[]); // Uma array
   void              operator=(const CRow & r);         // Outro objeto CRow
   double            operator*(const CRow &o);          // Objeto CRow para multiplicação
  };
//+——————————————————————+
//| Construtor para inicializar uma linha com um array               |
//+——————————————————————+
void  CRow::CRow(const double &array[])
  {
   int size=ArraySize(array);
//— Se o array não está vazio
   if(size>0)
     {
      ArrayResize(m_array,size);
      //— Preencher com valores
      for(int i=0;i<size;i++)
         m_array[i]=array[i];
     }
//—
  }
//+——————————————————————+
//| Atribuir operação para o array                                   |
//+——————————————————————+
void CRow::operator=(const double &array[])
  {
   int size=ArraySize(array);
   if(size==0) return;
//— Preencher array com valores
   ArrayResize(m_array,size);
   for(int i=0;i<size;i++) m_array[i]=array[i];
//— 
  }
//+——————————————————————+
//| Operação de atribuição para CRow                                 |
//+——————————————————————+
void CRow::operator=(const CRow  &r)
  {
   int size=r.Size();
   if(size==0) return;
//— Preencher array com valores
   ArrayResize(m_array,size);
   for(int i=0;i<size;i++) m_array[i]=r[i];
//— 
  }
//+——————————————————————+
//| Operador de multiplicação por outra linha                        |
//+——————————————————————+
double CRow::operator*(const CRow &o)
  {
   double res=0;
//— Verificações
   int size=Size();
   if(size!=o.Size() || size==0)
     {
      Print(__FUNCSIG__,”: Falha ao multiplicar duas matrizes, elas são de tamanhos diferentes”);
      return(res);
     }
//— Multiplicar arrays elemento a elemento e adicionar os produtos
   for(int i=0;i<size;i++)
      res+=m_array[i]*o[i];
//— Resultado
   return(res);
  }
//+——————————————————————+
//| Retorno da representação da string formatada                     |
//+——————————————————————+
string CRow::String(void) const
  {
   string out=””;
//— Se o tamanho do array é maior do que zero
   int size=ArraySize(m_array);
//— Trabalhamos apenas com números diferentes de zero dos elementos array
   if(size>0)
     {
      out=”{“;
      for(int i=0;i<size;i++)
        {
         //— Recolher os valores para a string
         out+=StringFormat(” %G;”,m_array[i]);
        }
      out+=” }”;
     }
//— Resultado
   return(out);
  }
//+——————————————————————+
//| Class “Matrix”                                                   |
//+——————————————————————+
class CMatrix
  {
private:
   CRow              m_rows[];
 
public:
   //— Construtores e um destrutor
                     CMatrix(void);
                     CMatrix(int rows)  { ArrayResize(m_rows,rows);             }
                    ~CMatrix(void){};
   //— Obter os tamanhos de matriz
   int               Rows()       const { return(ArraySize(m_rows));            }
   int               Cols()       const { return(Rows()>0? m_rows[0].Size():0); }
   //— Retorna o valor da coluna na forma da linha Crow
   CRow              GetColumnAsRow(const int col_index) const;
   //— Retorna uma string com valores de matriz
   string            String(void) const;
   //— O operador de indexação retorna uma string pelo seu número
   CRow *operator[](int i) const        { return(GetPointer(m_rows[i]));        }
   //— Operador de adição
   CMatrix           operator+(const CMatrix &m);
   //— Operador de multiplicação
   CMatrix           operator*(const CMatrix &m);
   //— Operador de atribuição
   CMatrix          *operator=(const CMatrix &m);
  };
//+——————————————————————+
//| Um construtor padrão, cria um array de linhas de tamanho zero    |
//+——————————————————————+
CMatrix::CMatrix(void)
  {
//— O número zero das linhas na matriz
   ArrayResize(m_rows,0);
//—  
  }
//+——————————————————————+
//| Retorna o valor da coluna na forma de CRow                       |
//+——————————————————————+
CRow  CMatrix::GetColumnAsRow(const int col_index) const
  {
//— A variável para obter os valores a partir da coluna
   CRow row();
//— O número de linhas na matriz
   int rows=Rows();
//— Se o número de linhas maior do que zero, executar a operação
   if(rows>0)
     {
      //— Um array para receber os valores da coluna com índice col_indez
      double array[];
      ArrayResize(array,rows);
      //— Preenchendo o array
      for(int i=0;i<rows;i++)
        {
         //— Verificar o número da coluna para a linha i – que podem ultrapassar os limites da matriz
         if(col_index>=this[i].Size())
           {
            Print(__FUNCSIG__,”: Erro! Número da coluna “,col_index,”> tamanho da linha “,i);
            break; // linha não inicializará o objeto
           }
         array[i]=this[i][col_index];
        }
      //— Criar uma linha CRow baseada nos valores do array
      row=array;
     }
//— Resultado
   return(row);
  }
//+——————————————————————+
//| Adicionar duas matrizes                                          |
//+——————————————————————+
CMatrix CMatrix::operator+(const CMatrix &m)
  {
//— O número de linha e colunas na matriz passada
   int cols=m.Cols();
   int rows=m.Rows();
//— A matriz recebe os resultados da adição
   CMatrix res(rows);
//— Os tamanhos de matriz devem coincidir
   if(cols!=Cols() || rows!=Rows())
     {
      //— Adição impossível
      Print(__FUNCSIG__,”: Falha para adicionar duas matrizes, seus tamanhos são diferentes”);
      return(res);
     }
//— Array auxiliar
   double arr[];
   ArrayResize(arr,cols);
//— Atravessar as linhas para adicionar
   for(int i=0;i<rows;i++)
     {
      //— Escrever os resultados da adição das strings matriz no array
      for(int k=0;k<cols;k++)
        {
         arr[k]=this[i][k]+m[i][k];
        }
      //— Colocar o array para a linha matriz
      res[i]=arr;
     }
//— retorna o resultado da adição de matrizes
   return(res);
  }
//+——————————————————————+
//| Multiplicação de duas matrizes                                   |
//+——————————————————————+
CMatrix CMatrix::operator*(const CMatrix &m)
  {
//— Número de colunas da primeira matriz, número de linhas transmitidas na matriz
   int cols1=Cols();
   int rows2=m.Rows();
   int rows1=Rows();
   int cols2=m.Cols();
//— Matriz para receber o resultado da adição
   CMatrix res(rows1);
//— Matrizes devem ser coordenadas
   if(cols1!=rows2)
     {
      //— Multiplicação impossível
      Print(__FUNCSIG__,”: Falha para multiplicar duas matrizes, formato não é compatível “
            “- o número de colunas no primeiro fator deveria ser igual ao número de linhas na segunda”);
      return(res);
     }
//— Array auxiliar
   double arr[];
   ArrayResize(arr,cols1);
//— Preencher as linhas na multiplicação da matriz
   for(int i=0;i<rows1;i++)// Atravessar as linhas
     {
      //— Restabelecer o array recebido
      ArrayInitialize(arr,0);
      //— Atravessar elementos na linha
      for(int k=0;k<cols1;k++)
        {
         //— Levar valores da coluna k da matriz m para CRow
         CRow column=m.GetColumnAsRow(k);
         //— Multiplicar duas linhas e escrever o resultado da multiplicação escalar dos vetroes no i-ésimo elemento
         arr[k]=this[i]*column;
        }
      //— colocar array arr[] na linha i-th da matriz
      res[i]=arr;
     }
//— Retornar o produto das duas matrizes
   return(res);
  }
//+——————————————————————+
//| Operação de atribuição                                           |
//+——————————————————————+
CMatrix *CMatrix::operator=(const CMatrix &m)
  {
//— Preencher e defineir o número de linhas
   int rows=m.Rows();
   ArrayResize(m_rows,rows);
//— Preencher nossas linhas com valores das linhas da matriz anterior
   for(int i=0;i<rows;i++) this[i]=m[i];
//—
   return(GetPointer(this));
  }
//+——————————————————————+
//| Representação string da matriz                                   |
//+——————————————————————+
string CMatrix::String(void) const
  {
   string out=””;
   int rows=Rows();
//— Formar string por string
   for(int i=0;i<rows;i++)
     {
      out=out+this[i].String()+”\r\n”;
     }
//— Resultado
   return(out);
  }

Também Veja

SobrecargaOperações AritméticasSobrecarga de FunçãoRegras de Precedência

Descrição de Funções Externas

As funções externas definidas em outro módulo devem ser explicitamente descritas. A descrição inclui o tipo retornado, o nome da função e uma série dos parâmetros de entrada com os seus tipos. A ausência de tal descrição pode levar a erros ao compilar, construir e executar um programa. Ao descrever um objeto externo, use a palavra-chave #import indicando o módulo.

Exemplos:

#import “user32.dll”
  int     MessageBoxW(int hWnd ,string szText,string szCaption,int nType);
  int     SendMessageW(int hWnd,int Msg,int wParam,int lParam);
#import “lib.ex5”
  double  round(double value);
#import

Com a ajuda do import, é fácil descrever funções que são chamadas de DLL externas ou de bibliotecas (libraries) EX5 compiladas. Bibliotecas (libraries) EX5 são arquivos ex5 compilados, que têm a propriedade library. Somente funções descritas com o modificador export pode ser importadas de bibliotecas (libraries) EX5.

Lembre-se de que as bibliotecas DLL e EX5 devem ter nomes diferentes (independentemente dos diretórios em que estão localizados) se forem importados juntos. Todas as funções importadas têm a resolução de escopo correspondente à biblioteca “file name”.

Exemplo:

#import “kernel32.dll”
   int GetLastError();
#import “lib.ex5”
   int GetLastError();
#import
 
class CFoo
  {
public:
   int            GetLastError() { return(12345); }
   void           func()
     {
      Print(GetLastError());           // Chamada do método de classe
      Print(::GetLastError());         // chamar a função MQL5
      Print(kernel32::GetLastError()); // Chamada da função de biblioteca DLL a partir do kernel32.dll
      Print(lib::GetLastError());      // Chamada da função da biblioteca EX5 lib.ex5
     }
  };
 
void OnStart()
  {
   CFoo foo;
   foo.func();
  }

Também Veja

Sobrecarga (Overload)Funções VirtuaisPolimorfismo

Exportação de Funções

Uma função declarada em um programa MQL5 com o pós-modificador export pode ser usada em um outro programa MQL5. Tal função é chamada exportável, e ela pode ser chamada de outros programas após compilação.

int Function() export
  {
  }

Este modificador ordena o compilador a adicionar a função na tabela de funções EX5 exportada por este arquivo ex5. Somente funções com tal modificador podem ser acessíveis (“visíveis”) a partir de outros programas MQL5.

A propriedade library diz ao compilador que o arquivo-EX5 será uma biblioteca (library), e o compilador o exibirá no cabeçalho da EX5.

Todas as função que são planejadas a serem exportáveis devem ser marcadas com o modificador export.

Também Veja

Sobrecarga (Overload)Funções VirtuaisPolimorfismoDescrição de Funções ExternasFunções de Manipulação de Evento

Funções de Manipulação de Evento

A linguagem MQL5 fornece processamento de alguns eventos pré-definidos. Funções para manipulação destes eventos devem ser definidas em um programa MQL5; nome da função, tipo de retorno, composição dos parâmetros (se existir algum) e seus tipos devem estar rigorosamente em conformidade com a descrição da função de handler (manipulador) de evento.

O handler (manipulador) de evento do terminal cliente identifica funções, manipulando este ou aquela evento, pelo tipo de valor de retorno e tipo de parâmetros. Se outros parâmetros, que não correspondem às descrições abaixo, são especificadas para uma função correspondente, ou outro tipo de retorno é indicado para ela, tal função não será usada como um handler (manipulador) de evento.

OnStart #

A função OnStart() é o handler (manipulador) do evento Start ,que é automaticamente gerado somente para execuções de scripts. Ela dever ser do tipo void, sem parâmetros:

void OnStart();

Para a função OnStart(), o tipo de retorno int pode ser especificado.

OnInit #

A função OnInit() é o handler (manipulador) do evento Init. Ela deve ser do tipo void ou int, sem parâmetros:

void OnInit();

O evento Init é gerado imediatamente após um Expert Advisor ou um indicador ser baixado; este evento não é gerado para scripts. A função OnInit() é usada para inicialização. Se OnInit() tiver o tipo int de valor de retorno, o código de retorno não-zero significa inicialização sem sucesso e é gerado o evento Deinit com o código do motivo da desinicialização REASON_INITFAILED.

Para otimizar os parâmetros de entrada de um Expert Advisor, é recomendado usar valores da enumeração ENUM_INIT_RETCODE como código de retorno. Esses valores são usados para organizar o curso da otimização, incluindo a seleção dos mais apropriados agentes de teste. Durante a inicialização de um Expert Advisor, antes do início do teste, você pode solicitar informação sobre a configuração e recursos de um agente (o número de cores, quantidade de memória livre, etc) usando a função TerminalInfoInteger(). Baseado nestas informações obtidas, pode-se tanto permitir usar este agente de teste, ou rejeitar usá-lo durante a otimização deste Expert Advisor.

ENUM_INIT_RETCODE

IdentificadorDescrição
INIT_SUCCEEDEDInicialização bem sucedida, teste do Expert Advisor pode continuar.Este código significa o mesmo que um valor null = o Expert Advisor foi inicializado com sucesso no Provador de Estratégia.
INIT_FAILEDInicialização com falha; não existe motivo para continuar testando por cause de erros fatais. Por exemplo, falha em criar um indicador que é requerido para o funcionamento do Expert Advisor.Este valor de retorno significa o mesmo que um valor diferente de zero – inicialização do Expert Advisor pelo Provador de Estratégia falhou.
INIT_PARAMETERS_INCORRECTEste valor significa a incorreta definição da entrada de parâmetros. A seqüência de resultado contendo o código de retorno é destacado em vermelho na tabela de otimização geral.O teste para o dado conjunto de parâmetros do Expert Advisor não será executado, o agente é liberado para receber uma nova tarefa.Depois de receber este valor, o testador de estratégia com segurança não vai passar essa tarefa para que outros agentes tentem novamente.
INIT_AGENT_NOT_SUITABLENenhum erro durante a inicialização, mas por alguma razão o agente não é adequado para o teste. Por exemplo, ausência de memória suficiente, sem support ao OpenCL, etc.Após este código de retorno, o agente não receberá tarefas até o fim desta otimização.

A função OnInit() do tipo void sempre indica inicialização bem sucedida.

OnDeinit #

A função OnDeinit() é chamada durante a desinicialização e é o handler (manipulador) do evento Deinit. Ela deve ser declarada com o tipo void e ter um parâmetro do tipo const int, que contém o código do motivo da desinicialização. Se um tipo diferente é declarado, o compilador gerará um aviso e a função não será chamada. Para scripts o evento Deinit não é gerado e portanto a função OnDeInit() não pode ser usada em scripts.

void OnDeinit(const int reason);

O evento Deinit é gerado para Expert Advisors e indicadores nos seguintes casos:

  • antes de uma reinicialização devido à mudança de ativo (symbol) ou período do gráfico no qual o programa MQL5 está anexado;
  • antes de uma reinicialização devido à mudança de parâmetros de entrada;
  • antes de descarregar o programa MQL5.

OnTick #

O evento NewTick é gerado para Expert Advisors somente quanto um novo preço (tick) para um ativo é recebido pelo gráfico no qual o Expert Advisor está anexado. É inútil definir a função OnTick() num indicador personalizado ou script, porque o evento NewTick não é gerado para eles.

O evento Tick é gerado apenas para Expert Advisors, mas isso não significa que Expert Advisors requer a função OnTick(), uma vez que não são apenas os eventos NewTick gerados para Expert Advisors, mas também são gerados os eventos de Timer, BookEvent e ChartEvent. Ela deve ser declarada com o tipo void, sem parâmetros:

void OnTick();

OnTimer #

A função OnTimer() é chamada quando o evento Timer ocorre, que é gerado pelo timer do sistema somente para Expert Advisors e indicadores – ela não pode ser usada em scprits. A freqüência de ocorrência do evento é definida na subscrição de notificações deste evento através da função EventSetTimer().

Você pode desfazer a subscrição de receber eventos de timer para um Expert Advisor particular usando a função EventKillTimer(). A função de ser definida com o tipo void, sem parâmetros:

void OnTimer();

É recomendável chamar a função EventSetTimer() uma vez na função OnInit(), e a função EventKillTimer() deve ser chamada uma vez em OnDeinit().

Todo Expert Advisor, assim como todo indicador funciona com seu próprio timer e recebe eventos apenas a partir dele. Tão logo um programa MQL5 para de funcionar, o timer é destruído de forma forçada, se ele foi criado mas não desabilitado pela função EventKillTimer().

OnTrade #

A função é chamada quando o evento Trade ocorre, que surge quando você muda a lista de ordens postadas e posições abertaso histórico de ordens e histórico de operações (deals). Quando uma atividade de negociação (trade) é realizada (abertura de ordem pendente, abertura/fechamento de posição, definição de stop, disparo de ordem pendente, etc.) o histórico de ordens e operações (deals) e/ou a lista de posições e ordens correntes são por conseqüência alterados.

void OnTrade();

Os usuários devem implementar de forma independente no código a verificação do estado de uma conta de negociação quanto tal evento é recebido (se isto é requerido pelas condições da estratégia de negócio). Se a chamada da função OrderSend() foi concluída com sucesso e retornou um valor de true, isso significa que o servidor de negociação postou a ordem na fila de execução e atribuiu um número de bilhetagem (ticket number) nele. Tão logo o servidor processe esta ordem, o evento Trade será gerado. E se um usuário lembrar valor da bilhetagem (ticket), ele/ela será capaz de descobrir o que aconteceu com a ordem usando este valor na função OnTrade().

OnTradeTransaction #

Ao realizar algumas ações específicas em uma conta de negociação, seu estado muda. Tais ações includem:

  • Envio de uma solicitação de negociação a partir de algum aplicativo MQL5 no terminal usando as funções OrderSend e OrderSendAsync e sua posterior execução.
  • Envio de uma solicitação de negociação por meio da interface gráfica do terminal e sua posterior execução.
  • Ativação de ordens pendentes e ordens de stop no servidor.
  • Realização de operações no lado de um servidor de negociação.

As seguintes transações de negociação são realizadas como resultado destas ações:

  • tratamento de uma solicitação de negociação
  • mudança de ordens de abertura
  • mudança de histórico de ordens
  • mudança de histórico de operações (deals)
  • mudança de posições

Por exemplo, ao enviar uma ordem de compra de mercado, ela é tratada, uma ordem de compra apropriada é criada para a conta, a ordem é então executada e removida da lista de ordens em aberto, e então ela é adicionada ao histórico de ordens, uma apropriada operação (deal) é adicionada ao histórico e uma nova posição é criada. Todas estas ações são transações de negociação. A chegada de tal transação no terminal é um evento TradeTransaction. Ele chama o handler (manipulador) de evento OnTradeTransaction.

void  OnTradeTransaction(
   const MqlTradeTransaction&    trans,        // estrutura das transações de negócios
   const MqlTradeRequest&        request,      // estrutura solicitada
   const MqlTradeResult&         result        // resultado da estrutura
   );

O handler (manipulador) contém três parâmetros:

  • trans – este parâmetro obtém a estrutura MqlTradeTransaction descrevendo uma transação de negociação aplicada a uma conta de negócio.
  • request – este parâmetro obtém a estrutura MqlTradeRequest descrevendo uma solicitação de negócio;
  • result – este parâmetro obtém a estrutura MqlTradeResult descrevendo o resultado da execução de uma solicitação de negociação.

Os últimos dois parâmetros, request e result, são preenchidos por valores somente para uma transação de tipo TRADE_TRANSACTION_REQUEST, dados sobre uma transação podem ser recebidos a partir do parâmetro do tipo da variável trans. Note que neste caso, o campo request_id na variável result contém o ID da solicitação de negócio, após a execução da transação de negociação, descrita na variável trans, ter sido realizada. O identificador da solicitação (Request ID) permite associar a ação realizada (chamada de funções OrderSend ou OrderSendAsync) com o resultado da ação enviado para OnTradeTransaction().

Uma solicitação de negociação manualmente enviada a partir do terminal ou via funções OrderSend()/OrderSendAsync() podem gerar várias transações consecutivas no servidor de negócios. A prioridade de chegada dessas transações no terminal não é garantida. Assim, você não deve esperar que um grupo de transações chegará após um outro grupo ao desenvolver seu algoritmo de negociação.

Todos os tipo de transações de negociação são descritas na enumeração ENUM_TRADE_TRANSACTION_TYPE.A estrutura MqlTradeTransaction descrevendo um transação de negociação é preenchida de diferentes formas dependendo do tipo de transação. Por exemplo, somente o campo de tipo (tipo de transação de negociação) deve ser analisado para transações do tipo TRADE_TRANSACTION_REQUEST. O segundo e terceiro parâmetros (request e result) da função OnTradeTransaction deve ser analisado para dados adicionais. Para informações adicionais, veja Estrutura de uma Transação de Negociação.Uma descrição de transação de negociação não entrega todas as informações disponíveis relativas a ordens, operações (deals) e posições (por exemplo, comentários). As funções OrderGet*HistoryOrderGet*HistoryDealGet* e PositionGet* devem ser usadas para obter informações adicionais.

Após aplicar transações de negociação em uma conta de cliente, elas são consistentemente postadas na fila de transações de negócio do terminal, a partir da qual são consistentemente enviados para o ponto de entrada OnTradeTransaction na ordem de chegada no terminal.

Ao tratar transações de negociação por um Expert Advisor usando o handler OnTradeTransaction (Manipulador sobre Transação de Comércio), o terminal continua manipulando as transações de negociação recém chegadas. Portanto, o estado de uma conta de negociação pode mudar durante uma operação OnTradeTransaction. Por exemplo, enquanto um programa MQL5 manipula um evento para adicionar uma nova ordem, ela pode ser executada, deletada da lista das abertas e movida para o histórico. Mais adiante, o aplicativo será notificado destes eventos.

O comprimento da fila de transações compreende 1024 elementos. Se OnTradeTransaction tratar uma nova transação por muito tempo, as transações mais antigas na fila podem ser substituídas pelas novas.

De forma geral, não existe um proporção precisa entre o número de chamadas de OnTrade e OnTradeTransactions. Uma chamada OnTrade corresponde a uma ou várias chamadas OnTradeTransactions.OnTrade é chamada após apropriadas chamadas OnTradeTransaction.

OnTester #

A função OnTester() é o handler (manipulador) do evento Tester que é automaticamente gerado após um teste de histórico de um Expert Advisor no intervalo escolhido ter terminado. A função deve estar definida com o tipo double, sem parâmetros:

double OnTester();

A função é chamada logo antes da chamada de OnDeinit() e tem o mesmo tipo do valor de retorno – double. OnTester() pode ser usado apenas no teste de Expert Advisors. Seu principal propósito é calcular um certo valor que é usado como o critério max customizado na otimização genética de parâmetros de entrada.

Na otimização genética, a ordenação descendente é aplica aos resultados de uma geração. Isto é, do ponto de vista do critério de otimização, os melhores resultados são aqueles com o maiores valores (os valores do critério de otimização max customizado retornados pela função OnTester são levados em consideração). Em tal ordenação, os piores valores são posicionados no final e posteriormente jogados fora e não participam na formação da nova geração.

OnTesterInit #

A função OnTesterInit() é o handler( manipulador) do evento TesterInit, que é automaticamente gerado antes de iniciar a otimização do Expert Advisor no Provador de Estratégia. A função deve ser definida com o tipo void. Ele não tem parâmetros:

void OnTesterInit();

Com o início da otimização, um Expert Advisor com o handler (manipulador) OnTesterInit() ou OnTesterPass() é automaticamente carregado em um gráfico separado do terminal com o ativo e período especificados no Provador de Estratégia, e recebe o evento TesterInit. A função é usada para inicializar o Expert Advisor antes de iniciar a otimização para posterior processamento dos resultados da otimização.

OnTesterPass #

A função OnTesterPass() é um handler (manipulador) do evento TesterPass, que é automaticamente gerado quanto um plano é recebido durando a otimização de um Expert Advisor no Provador de Estratégia. A função deve ser definida com o tipo void. Ele não tem parâmetros:

void OnTesterPass();

Um Expert Advisor com o handler (manipulador) OnTesterPass() é automaticamente carregado em um gráfico separado do terminal com o ativo/período especificados para teste, e obtém eventos TesterPass quando um plano é recebido durante uma otimização. A função é usada para tratamento dinâmico dos resultados de otimização “no local” sem precisar esperar pela sua conclusão. Planos são adicionados usando a função FrameAdd(), que pode ser chamada após o fim de um passo único no handler (manipulador) OnTester() .

OnTesterDeinit #

OnTesterDeinit() é um handler (manipulador) do TesterDeinit, que é automaticamente gerada após o fim da optimização de um Expert Advisor no Provador de Estratégia. A função deve ser definida com o tipo void. Ele não tem parâmetros:

void OnTesterDeinit();

Um Expert Advisor com o handler (manipulador) TesterDeinit() é automaticamente carregada em um gráfico no início da otimização, e recebe TesterDeinit após sua conclusão. A função é usada para um processamento final de todos os resultados da otimização.

OnBookEvent #

A função OnBookEvent() é o handler (manipulador) do BookEvent. BookEvent é gerado para Expert Advisors e indicadores somente quando a Profundidade do Mercado muda. Ela deve do tipo void e ter um parâmetro do tipo string:

void OnBookEvent (const string& symbol);

Para receber eventos BookEvent para qualquer ativo (symbol), você apenas precisa fazer uma pré-subscrição pra receber eventos para este ativo usando a função MarketBookAdd(). A fim de desfazer a subscrição de recebimento de eventos BookEvent para um particular ativo, chame MarketBookRelease().

Diferente de outros eventos, o evento BookEvent é por difusão (broadcast). Isso significa que se um Expert Advisor subscreve para receber eventos BookEvent usando MarketBookAdd, todos os outros Experts Advisors que tem o handler (manipulador) OnBookEvent() receberão este evento. É portanto necessário analisar o nome do ativo, que é passado para o handler (manipulador) através dos parâmetros const string& symbol.

OnChartEvent #

OnChartEvent() é o handler (manipulador) de um grupo de eventos ChartEvent:

  • CHARTEVENT_KEYDOWN – evento de uma teclada, quando a janela do gráfico está com foco;
  • CHARTEVENT_MOUSE_MOVE – eventos de movimento de mouse e eventos de click de mouse (se CHART_EVENT_MOUSE_MOVE=true é definido para o gráfico);
  • CHARTEVENT_OBJECT_CREATE – evento de criação de objeto gráfico (se CHART_EVENT_OBJECT_CREATE=true é definido para o gráfico);
  • CHARTEVENT_OBJECT_CHANGE – evento de mudança de um propriedade de objeto via janela diálogo de propriedades;
  • CHARTEVENT_OBJECT_DELETE – evento de exclusão de objeto gráfico (se CHART_EVENT_OBJECT_DELETE=true é definido para o gráfico);
  • CHARTEVENT_CLICK – evento de um click de mouse no gráfico;
  • CHARTEVENT_OBJECT_CLICK – evento de um click de mouse em um objeto gráfico pertencente ao gráfico;
  • CHARTEVENT_OBJECT_DRAG – evento de um movimento de objeto gráfico usando o mouse;
  • CHARTEVENT_OBJECT_ENDEDIT – evento da edição de texto finalizada na caixa de entrada do objeto gráfico LabelEdit;
  • CHARTEVENT_CHART_CHANGE – evento de mudanças de gráfico;
  • CHARTEVENT_CUSTOM+n – ID do evento do usuário, onde n está na faixa de 0 a 65535.
  • CHARTEVENT_CUSTOM_LAST – o último ID aceitável de um evento customizado (CHARTEVENT_CUSTOM+65535).

A função pode ser chamada somente em Expert Advisors e indicadores. A função deve ser de tipo void com 4 parâmetros:

void OnChartEvent(const int id,         // Evento ID
                  const long& lparam,   // Parâmetro de evento de tipo long
                  const double& dparam, // Parâmetro de evento de tipo double
                  const string& sparam  // Parâmetro de evento de tipo string
  );

Para cada tipo de evento, os parâmetros de entrada da função OnChartEvent() têm valores definidos que são requeridos para o processamento deste evento. Os eventos e valores passados através destes parâmetros são listados na tabela abaixo.

EventoValor do parâmetro idValor do parâmetro lparamValor do parâmetro dparamValor do parâmetro sparam
Evento de uma tecladaCHARTEVENT_KEYDOWNcódigo de uma tecla pressionadaRepita a contagem (o número de vezes que a tecla é repetida como um resultado de que o usuário pressiona a tecla)O valor da string de uma pequena máscara de descrever o estado de botões do teclado
Eventos de mouse (se a propriedade CHART_EVENT_MOUSE_MOVE=true está definida para o gráfico)CHARTEVENT_MOUSE_MOVEa coordenada Xa coordenada YO valor de string de uma máscara de bites descrevendo o estado de botões de mouse
Evento de criação de objeto gráfico (se CHART_EVENT_OBJECT_CREATE=true, então é definido para o gráfico)CHARTEVENT_OBJECT_CREATENome do objeto gráfico criado
Evento de mudança de uma propriedade de objeto via janela de diálogo de propriedadesCHARTEVENT_OBJECT_CHANGENome do objeto gráfico modificado
Evento de exclusão de objeto gráfico (se CHART_EVENT_OBJECT_DELETE=true está definido para o gráfico)CHARTEVENT_OBJECT_DELETENome do objeto gráfico excluído
Evento de um click de mouse no gráficoCHARTEVENT_CLICKa coordenada Xa coordenada Y
Evento de um click de mouse num objeto gráfico pertencente ao gráficoCHARTEVENT_OBJECT_CLICKa coordenada Xa coordenada YNome do objeto gráfico, na qual o evento ocorreu
Evento de um objeto gráfico arrastado usando o mouseCHARTEVENT_OBJECT_DRAGNome do objeto gráfico movido
Evento da edição de texto finalizada na caixa de entrada do objeto gráfico LabelEditCHARTEVENT_OBJECT_ENDEDITNome do objeto gráfico LabelEdit, cuja edição de texto foi concluída
Evento de mudanças de gráficoCHARTEVENT_CHART_CHANGE
ID do evento de usuário sob N númeroCHARTEVENT_CUSTOM+NValor definido pela função EventChartCustom()Valor definido pela função EventChartCustom()Valor definido pela função EventChartCustom()

OnCalculate #

A função OnCalculate() é chamada somente em indicadores customizados quando é necessário calcular os valores do indicador pelo evento Calculate. Isso geralmente acontece quando um novo preço (tick) é recebido para o ativo, de cujo indicador é calculado. Não é necessário que este indicador esteja anexado a qualquer gráfico de preço deste ativo.

A função OnCalculate() deve retornar um tipo int. Existem duas possíveis definições. Dentro de um indicador você não pode usar ambas as versões da função.

A primeira forma é destinado para aqueles indicadores que podem ser calculados com um único buffer de dados. Um exemplo de tal indicador é a Média Móvel Customizada (Custom Moving Average).

int OnCalculate (const int rates_total,      // tamanho do array price[]
                 const int prev_calculated,  // barras tratadas na chamada anterior
                 const int begin,            // a partir de onde começam os dados significativos
                 const double& price[]       // array a ser calculado
   );

Assim como o array price[], uma das série de preço ou um buffer calculado de algum indicador pode ser passado. Para determinar a direção da indexação no array price[], chame ArrayGetAsSeries(). A fim de não depender de valores default, você deve incondicionalmente chamar a função ArraySetAsSeries() para aqueles arrays que você espera utilizar.

Uma série de tempo necessária ou um indicador, para ser usado como o array price[], deve ser selecionado pelo usuário na guia “Parâmetros” ao iniciar o indicador. Para fazer isso, você deve especificar o necessário item no lista drop-down do campo “Aplicar a”.

Selecionando uma série de tempo para calcular um indicador

Para receber valores de um indicador customizado a partir outros programas mql5, a função iCustom() é usada, que retorna o manuseio do indicador para operações subseqüentes. Você pode também especificar o price[] array apropriado ou o manuseio de outro indicador. Este parâmetro deve ser transmitido por último na lista de variáveis de entrada do indicador customizado.
Exemplo:

void OnStart()
  {
//—
   string terminal_path=TerminalInfoString(STATUS_TERMINAL_PATH);
   int handle_customMA=iCustom(Symbol(),PERIOD_CURRENT, “Custom Moving Average”,13,0, MODE_EMA,PRICE_TYPICAL);
   if(handle_customMA>0)
      Print(“handle_customMA = “,handle_customMA);
   else
      Print(“Pode abrir ou não o arquivo EX5 ‘”+terminal_path+”\\MQL5\\Indicators\\”+”Custom Moving Average.ex5′”);
  }

Neste exemplo, o último parâmetro passado é o valor PRICE_TYPICAL (da enumeração ENUM_APPLIED_PRICE), que indica que o indicador customizado será construído baseado em preços típicos obtidos como (High+Low+Close)/3. Se este parâmetro não for especificado, o indicador é construído baseado em valores de PRICE_CLOSE, isto é, preços de fechamento de cada barra.

Outro exemplo que mostra a passagem de um handler (manipulador) de indicador como o último parâmetro para especificar o array price[], é dado na descrição da função iCustom().
 

A segunda forma é destinada para todos os outros indicadores, na qual mais de uma série de tempo é usada nos cálculos.

int OnCalculate (const int rates_total,      // tamanho da série de preços de entrada series
                 const int prev_calculated,  // barras tratadas na chamada anterior
                 const datetime& time[],     // Hora
                 const double& open[],       // Open (abertura)
                 const double& high[],       // High (máximo)
                 const double& low[],        // Low (mínimo)
                 const double& close[],      // Close (fechamento)
                 const long& tick_volume[],  // Volume de Tick
                 const long& volume[],       // Volume Real
                 const int& spread[]         // Spread
   );

Os parâmetros open[], high[], low[] and close[] contém os arrays com preços de abertura, preços de máximo e mínimo e preços de fechamento da janela de tempo corrente. O parâmetro time[] contém um array com valores de hora de abertura, o parâmetro spread[] tem um array contendo o histórico de spreads (se algum spread é fornecido para o ativo negociado). Os parâmetros volume[] e tick_volume[] contêm o histórico de volume de negociação e tick, respectivamente.

Par determinar a direção de indexação de time[], open[], high[], low[], close[], tick_volume[], volume[] e spread[], chame ArrayGetAsSeries(). A fim de não depender de valores default, você deve incondicionalmente chamar a função ArraySetAsSeries() para aqueles arrays que você esperar utilizar.

O primeiro parâmetro rates_total contém o número de barras disponíveis no indicador para cálculo, e corresponde ao número de barras disponíveis no gráfico.

Devemos notat a conexão entre o valor de retorno de OnCalculate() e o segundo parâmetro de entrada prev_calculated. Durante a chamada da função, o parâmetro prev_calculated contém um valor retornado pelo OnCalculate() durante a chamada anterior. Isso permite que algoritmos eficientes calculem o indicador customizado de forma a evitar cálculos repetidos naquelas barras que não tiveram mudança deste a execução anterior desta função.

Para isso, é geralmente suficiente retornar o valor do parâmetro rates_total, que contém o número de barras da chamada corrente da função. Se desde a última chamada da função OnCalculate() os dados de preço mudarem (um histórico mais antigo baixado ou brancos no histórico preenchidos), o valor do parâmetro de entrada prev_calculated será definido para zero pelo terminal.

Observação: se OnCalculate retornar zero, então os valores do indicador não são mostrados na Janela de Dados do terminal cliente.

Para entender isso melhor, seria útil iniciar o indicador, cujo código está anexado abaixo.

Exemplo indicador:

#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
//—- plotar Linha
#property indicator_label1  “Line”
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDarkBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//— buffers do indicador
double         LineBuffer[];
//+——————————————————————+
//| Função de inicialização do indicador customizado                 |
//+——————————————————————+
int OnInit()
  {
//— mapeamento de buffers do indicador
   SetIndexBuffer(0,LineBuffer,INDICATOR_DATA);
//—
   return(INIT_SUCCEEDED);
  }
//+——————————————————————+
//| Função de iteração do indicador customizado                      |
//+——————————————————————+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime& time[],
                const double& open[],
                const double& high[],
                const double& low[],
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[])
  {
//— Obtenção do número de barras disponíveis para o ativo corrente e período do gráfico
   int bars=Bars(Symbol(),0);
   Print(“Bars = “,bars,”, rates_total = “,rates_total,”,  prev_calculated = “,prev_calculated);
   Print(“time[0] = “,time[0],” time[rates_total-1] = “,time[rates_total-1]);
//— valor retorno de prev_calculated para a próxima chamada
   return(rates_total);
  }
//+——————————————————————+

Também Veja

Programas em ExecuçãoEventos do Terminal ClienteTrabalhando com Eventos

VARIAVEIS

https://www.mql5.com/pt/docs/basis/variables

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s