Trabalhando Com Doubles no MQL4

6 outubro 2015, 10:41
MetaQuotes Software Corp.
1
1 364

Introdução

A programação MQL abre novas oportunidades para a negociação automática, muitas pessoas em todo o mundo já estão aproveitando isto.

Quando estamos escrevendo um Expert Advisor para negociação, devemos ter certeza de que ele vai trabalhar corretamente.

Muitos novatos às vezes têm algumas perguntas quando ocorrem resultados de alguns cálculos matemáticos diferentes do esperado. O programa é compilado e pode funcionar, mas não como deveria. Então eles verificam o código de novo e de novo, encontrando novos "erros" na linguagem, na implementação, em funções, etc.

Na maioria dos casos, uma análise cuidadosa mostra que a linguagem mql4 e o compilador estão trabalhando corretamente, porém o código é que tem um pequeno erro e isto pode levar um longo tempo para encontrar o problema e corrigir.

Neste artigo vamos considerar erros típicos de programação que ocorrem durante o trabalho com números tipo double nos programas MQL4.


1. Verificando os valores numéricos

Para depurar e verificar os resultados do cálculo você pode usar a função DoubleToStrMorePrecision (número tipo double, int precision) da biblioteca padrão stdlib.mq4, permitindo o controle dos valores numéricos dos números tipo double da precisão especificada.

Esta função permitirá poupar tempo na procura de possíveis erros.

Um exemplo:

#include <stdlib.mqh>
int start()
  {
   double a=2.0/3;
   Alert("Standard output:",a,", 8 digits precision:",DoubleToStr(a,8),", 15 digits precision:", DoubleToStrMorePrecision(a,15));
   return(0);
  }

Resultado:

A saída padrão:0.6667, 8 dígitos de precisão:0.66666667, 15 dígitos de precisão:0.666666666666667

Em alguns casos, para mostrar os valores dos números tipo double (por exemplo, em Print, Alert, Comment) é melhor usar as funções DoubleToStr e DoubleToStrMorePrecision (a partir de stdlib.mq4) para apresentar valores mais precisos, em vez do padrão da saída de 4 dígitos de precisão.

Por exemplo:

#include <stdlib.mqh>
int start()
  {
   double a=2.0/100000;
   Alert("Standard output=",a,", More precise output=",DoubleToStrMorePrecision(a,15));
   return(0);
  }

retorna: "Standard output=0, More precise output=0.000020000000000".


2. Exatidão da precisão dos dígitos decimais

Por causa do formato de ponto flutuante da precisão double, existe uma precisão limitada do seu armazenamento.

Por exemplo, se assumirmos que temos uma precisão ilimitada, como na teoria, para qualquer número double A e B, as seguintes expressões são sempre válidas:

(A/B)*(B)=A,

A-(A/B)*B=0,

(A/B)*(B/A)=1 etc.

A precisão do armazenamento de dígitos decimais em computador é dependente do tamanho da fração e limitada a 52 bits. Para ilustrar esse fato, vamos considerar o seguinte exemplo.

No primeiro ciclo de (i) que estão calculando o fatorial de 23 (produto dos números inteiros de 2 a 23), o resultado é: 23! =25852016738884976640000. O resultado é armazenado na variável a do tipo double.

No ciclo seguinte (j), que está dividindo o valor do resultado a por todos os inteiros de 23 para 2. Então podemos esperar que a=1.

#include <stdlib.mqh>
int start()
  {
   int maxfact=23;
   double a=1;
   for (int i=2; i<=maxfact; i++) { a=a*i; }
   for (int j=maxfact; j>=2; j--) { a=a/j; }
   Alert(" a=",DoubleToStrMorePrecision(a,16));
   return(0);
  }

Em vez disso, temos:

a=1.0000000000000002

Como vemos nós temos um IMPRECISÃO em 16 dígitos.

Se aumentarmos o cálculo para 35!, vamos chegar a=0,9999999999999998.

A linguagem MQL tem uma função NormalizeDouble, que permite arredondar o número double para a precisão especificada.

As constantes do tipo double são armazenadas na memória de um modo semelhante às variáveis double, por conseguinte é necessário ter em conta o limite de 15 dígitos significativos na sua definição.

Mas não confunda a exatidão da precisão dos dígitos decimais com a precisão de cálculo dos números tipo doubles.

O alcance dos possíveis valores para os números double é muito amplo: a partir de -1.7*e-308 para 1.7*e308.

Vamos tentar estimar o menor expoente do número double.

int start()
  {
  double R=1;
  int minpwr=0;
  while (R>0) {R=R/10; minpwr--;}
  Alert(minpwr);
  return(0);
  }

O programa irá imprimir -324, mas temos de ter em conta os dígitos decimais na fração (+15) e nós entraremos num valor aproximado ao expoente do menor número double.


3. Função NormalizeDouble

A função NormalizeDouble (valor double, int dígitos) arredonda o valor do ponto flutuante com a precisão dada. Retorna valor normalizado do tipo double.

Por exemplo:

int start()
  {
   double a=3.141592653589;
   Alert("a=",DoubleToStr(NormalizeDouble(a,5),8));
   return(0);
  }

o resultado é:

a=3.14159000

Observe que nas operações de negociação é impossível usar preços não normalizados, cuja exatidão exceda pelo menos por um dígito, assim exigido por um servidor de negociação.
O StopLoss, TakeProfit e o valores de Preços para as ordens pendentes devem ser normalizados com a precisão, o valor é armazenado na variável predeterminada Digits.


4. Verificar a existência de igualdade de dois números double

Recomenda-se comparar dois números double usando a função CompareDoubles (double number1,double number2) da biblioteca stdlib.mq4:

//+------------------------------------------------------------------+
//| comparação correta de 2 doubles                                  |
//+------------------------------------------------------------------+
bool CompareDoubles(double number1,double number2)
  {
   if(NormalizeDouble(number1-number2,8)==0) return(true);
   else return(false);
  }

Essa função compara number1 e number2 do tipo double com uma precisão de até 8 dígitos decimais.

O exemplo:

#include <stdlib.mqh>
int start()
  {double a=0.123456781;
   double b=0.123456782; 
   if (CompareDoubles(a,b)) {Alert("They are equal");}
   else {Alert("They are different");}
  }

irá sair:

Eles são iguais

porque são diferente apenas no nono dígito.

Se necessário, você pode escrever sua própria função de comparação (com a precisão desejada) de uma forma similar.


5. Dividindo "integers"

É necessário lembrar que se estamos dividindo dois inteiros, então vamos obter um número inteiro como resultado.

Eis o código:

int start()
  {
   Alert(70/100);
   return(0);
  }

saída será 0, porque 70 e 100 são números inteiros.

Assim como na linguagem C/C++, na MQL o resultado da divisão de dois inteiros será um número inteiro, neste caso é 0.

No entanto, se o numerador ou denominador é o dobro (ou seja, tem uma parte fracionária), o resultado será do tipo double. Portanto Alert (70/100,0); vai apresentar o valor 0,7 como correto.

Por exemplo:

int start()
  { double a=1/3;
    double b=1.0/3;
   Alert("a=",a,", b=",b);
   return(0);
  }

irá sair "a=0, b=0.3333"


6. Conversão para números "integer" e "double"

Vamos considerar o código:

double xbaseBid=1.2972;
double xBid=1.2973;
double xPoint=0.0001;
int i = 100 + (xBid - xbaseBid)/xPoint;
Alert(i);

Nós obteremos 100, mas parece que ele deveria ser 101, por causa da equidade evidente: 0,0001/0,0001=1

O mesmo exemplo em C/C++:
double baseBid=1.2972,Bid=1.2973,Point=0.0001;
int i = 100 + (Bid - baseBid)/Point;
printf("%d\n",i);

também dá 100.

Para determinar a razão desse fato, vamos considerar o código:

double a=0.99999999999999;
int i = 100 + a;
Alert(i);

que dá i=100.

No entanto, se eu executar alguma melhoria da precisão para um:

double a=0.999999999999999;
int i = 100 + a;
Alert(i);

então vamos obter 101.

A razão desse fato é que números inteiros e duplos são complicados para baixos valores de precisão.

Portanto nas operações deste tipo é recomendado realizar o arredondamento das expressões semelhantes usando a função MathRound (valor double)

que retorna o valor double arredondado para o número inteiro mais próximo:

double baseBid=1.2972;
double xBid=1.2973;
double xPoint=0.0001;
int i = 100 + MathRound((xBid - baseBid)/xPoint);
Alert(i);

Nesse caso, vamos obter o valor correto 101.

Existe um erro comum no uso da função OrderModify, especialmente para a programação do Trailing Stop. A função OrderModify retorna um erro № 1: ERR_NO_RESULT no caso dos novos valores para as ordens serem as mesmas já definidas. Por este motivo é necessário a realização de uma verificação cuidadosa para a igualdade (usar NormalizeDouble) e de pontos nos cálculos de tamanho.

Recordamos que o terminal de cliente permite mudar os parâmetros da ordem apenas se os novos valores são diferentes dos já definidos, pelo menos em 1 ponto.


7. Características da função MathMod

Em MQL, a função MathMod (double v1 double v2) corresponde completamente à função fmod (double v1, v2 duplo) de MSVC6, devido a chamada direta à função "fmod", a partir do uso da biblioteca da C Runtime.

Em alguns casos, a função fmod de MSVC6 (e também MathMod) fornece resultados errados.

Se você estiver usando a função MathMod em seus programas, por favor substitua-a para a função a seguir:

double MathModCorrect(double a, double b)
{ int tmpres=a/b;
return(a-tmpres*b);
}

Note que esta observação é somente para MQL4, MQL5 calcula esta função pela sua definição matemática.


Conclusão

Note que a lista não é exaustiva, se você encontrou algo novo que não tenha sido descrito aqui, coloque nos comentários.

Se você ainda não encontrou uma solução, descreva o problema nos comentários, adicione o seu código e profissionais MQL da comunidade irão ajudá-lo.


Traduzido do russo pela MetaQuotes Software Corp.
Artigo original: https://www.mql5.com/ru/articles/1561

Últimos Comentários | Ir para discussão (1)
bertacini
bertacini | 23 mai 2018 em 14:46

Estou precisando saber a diferença em pontos entre resistência e suporte, mas preciso que retorne um numero inteiro.

 double resistencia = 111.459;

 double suporte = 110.116;
 int diferença = resistencia - suporte;

diferença = 1,134


Preciso que me retorne 1134 pontos.

Como posso resolver isso?

Obrigado

Como Foi Desenvolvido o Serviço de Sinais MetaTrader e Negociação social Como Foi Desenvolvido o Serviço de Sinais MetaTrader e Negociação social

Continuamos a melhorar o Serviço de Sinais, os mecanismos, adicionamos novas funções e corrigimos falhas. Os Serviços de Sinais MetaTrader de 2012 e o atual é como se fossem dois serviços completamente diferentes. Atualmente estamos implementando um serviço de hospedagem virtual nas nuvens, que consiste de uma rede de servidores para suportar versões específicas do terminal do cliente MetaTrader. Os Traders terão de preencher apenas 5 passos para locar uma cópia virtual de seu terminal com latência mínima de rede ao servidor de negociação da sua corretora, diretamente do terminal do cliente MetaTrader.

Por que é importante Atualizar o MetaTrader 4 na Versão Mais Recente? Por que é importante Atualizar o MetaTrader 4 na Versão Mais Recente?

A partir de 01 de agosto de 2014, os terminais MetaTrader 4 de computadores anteriores a Versão 600 não têm mais suporte. No entanto, muitos traders ainda trabalham com versões desatualizadas e não têm conhecimento das características da atualização da plataforma. Tivemos de fazer um grande esforço para este desenvolvimento, queremos seguir em frente com os traders e abandonarmos em definitivo as versões mais antigas. Neste artigo, iremos descrever as vantagens do novo terminal MetaTrader 4.

Utilizando Redes Neurais No MetaTrader Utilizando Redes Neurais No MetaTrader

Este artigo mostra como usar facilmente Redes Neurais em seu código MQL4, aproveitando a disponibilidade gratuita da melhor biblioteca artificial de rede neural (FANN) e empregando múltiplas redes neurais em seu código.

Desenvolvedores, Protejam-se a Si Mesmo! Desenvolvedores, Protejam-se a Si Mesmo!

Proteção da propriedade intelectual ainda é um grande problema. Este artigo descreve os princípios básicos de proteção dos programas MQL4. Usando estes princípios, você pode garantir que os resultados dos seus desenvolvimentos não sejam roubados por um ladrão, ou pelo menos para complicar o "trabalho" dele, tanto que ele simplesmente se recusará a fazê-lo.