English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Estimativas estatísticas

Estimativas estatísticas

MetaTrader 5Exemplos | 18 fevereiro 2014, 13:02
1 798 0
Victor
Victor

Introdução

Hoje em dia, você pode encontrar com frequência artigos e publicações com temas relacionados a econometria, previsões de séries de preço, escolha e estimativa de adequação de um modelo, etc. Mas, na maioria dos casos, o raciocínio é baseado em suposição que o leitor está familiarizado com os métodos de estimativas matemáticas e pode facilmente estimar os parâmetros estatísticos de uma sequência analisada.

Estimativa de parâmetros estatísticos de uma sequência é muito importante, desde que muitos dos modelos e métodos matemáticos são baseados em diferentes suposições. Por exemplo, normalidade da lei de distribuição ou valor de dispersão, ou outros parâmetros. Assim, quando analisando e realizando previsões de séries de tempo, nós precisamos uma ferramenta simples e conveniente que permite rápida e clara estimativa dos principais parâmetros estatísticos. Nesse artigo, vamos tentar criar essa ferramenta.

O arquivo descreve brevemente os parâmetros estatísticos mais simples de uma sequência aleatória e vários métodos de análise visual. Ele oferece a implementação desses métodos em MQL5 e os métodos de visualização dos resultados dos cálculos usando o aplicativo Gnuplot. De maneira alguma esse artigo pretendeu ser um manual ou referência; é por isso que pode conter certas familiaridades aceitas em relação a terminologia e definições.


Analisando parâmetros de uma amostra

Suponha que exista um processo estacionário existindo infinitamente no tempo, que pode ser representado como uma sequência de amostras discretas. Vamos chamar essa sequência de amostras como população geral. Uma parte de amostras selecionada da população geral sera chamada de uma amostragem da população geral ou uma amostragem de N amostras. Em adição à isso, suponha que nenhum parâmetro verdadeiro é conhecido, então nós vamos estimá-los com base em uma amostragem finita.


Evitando valores atípicos

Antes de começar a estimativa estatística de parâmetros, nós devemos notar que a precisão da estimativa pode ser insuficiente caso a amostragem contenha erros grosseiros (valores atípicos). Há uma enorme influência de valores atípicos na precisão de estimativas se a amostragem tem um volume pequeno. Valores atípicos são os valores que anormalmente divergem do meio da distribuição. Tais desvios podem ser causadas por diferentes eventos dificilmente prováveis ​​e os erros apareceram durante a coleta de dados estatísticos e formação da sequência.

é difícil tomar a decisão se filtrar os valores atípicos ou não, sendo que na maioria das vezes é impossível detectar claramente se o valor é um valor atípico ou pertence ao processo analisado. Então, se os valores atípicos são detectados e há uma decisão para filtrá-los, uma questão surge - o que devemos fazer com esses valores de erro? A variação mais lógica é excluir da amostragem, o que pode aumentar a precisão da estimativa de características estatísticas; mas você deve ter cuidado com a exclusão de valores atípicos da amostragem ao trabalhar com sequências de tempo.

Para ter a possibilidade de excluir valores atípicos de uma amostragem ou ao menos detectá-los, vamos implementar o algoritmo descrito no livro "Statistics for Traders" escrito por S.V. Bulashev.

De acordo com esse algoritmo, nós precisamos calcular cinco valores de estimativa do centro da distribuição:

  1. Mediana;
  2. Centro da faixa interquartil de 50% (midquartile range, MQR);
  3. Média aritmética da amostragem inteira;
  4. Média aritmética de faixa interquartil de 50% (média interquartil IQM);
  5. Centro da faixa (midrange) - determinado como o valor médio do valor máximo e do valor mínimo em uma amostragem.

Então, os resultados da estimativa do centro da distribuição são classificados em ordem crescente, em seguida, o valor médio ou o terceiro da ordem é escolhido como o centro de distribuição Xcen. Assim, a estimativa escolhida parece ser minimamente afetada por valores atípicos.

Além disso, utilizando a estimativa obtida do centro de distribuição Xce, vamos calcular o desvio padrão s, o excesso K e a taxa de censura de acordo com a fórmula empírica:

onde N é o número de amostras na amostragem (volume de amostragem).

Em seguida, os valores que se encontram fora da faixa:

serão contados como valores atípicos, assim eles devem ser excluídos da amostragem.

Esse método é descrito em detalhes no livro "Statistics for Traders", então, vamos direto para implementação do algoritmo. O algoritmo que permite detectar e excluir valores atípicos está implementado na função erremove().

Abaixo você pode encontrar o roteiro escrito para testar essa função.

//----------------------------------------------------------------------------
//                                                                erremove.mq5
//                                   Copyright 2011, MetaQuotes Software Corp.
//                                                         https://www.mql5.com
//----------------------------------------------------------------------------
#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#import "shell32.dll"
bool ShellExecuteW(int hwnd,string lpOperation,string lpFile,
                  string lpParameters,string lpDirectory,int nShowCmd);
#import
//----------------------------------------------------------------------------
// Script program start function
//----------------------------------------------------------------------------
void OnStart()
  {
  int i;
  double dat[100];
  double y[];
  
  srand(1);
  for(i=0;i<ArraySize(dat);i++)dat[i]=rand()/16000.0;
  
  dat[25]=3;           // Make Error !!!
  
  erremove(dat,y,1);
  
  }
//----------------------------------------------------------------------------
int erremove(const double &x[],double &y[],int visual=1)
  {
  int i,m,n;
  double a[],b[5];
  double dcen,kurt,sum2,sum4,gs,v,max,min;
  
  if(!ArrayIsDynamic(y))                           // Error
    {
    Print("Function erremove() error!");
    return(-1);
    }
  n=ArraySize(x);
  if(n<4)                                          // Error
    {
    Print("Function erremove() error!");
    return(-1);
    }
  ArrayResize(a,n);
  ArrayCopy(a,x);
  ArraySort(a);
  b[0]=(a[0]+a[n-1])/2.0;                          // Midrange
  m=(n-1)/2;
  b[1]=a[m];                                       // Median
  if((n&0x01)==0)b[1]=(b[1]+a[m+1])/2.0;
  m=n/4;
  b[2]=(a[m]+a[n-m-1])/2.0;                        // Midquartile range
  b[3]=0;
  for(i=m;i<n-m;i++)b[3]+=a[i];                    // Interquartile mean(IQM)
  b[3]=b[3]/(n-2*m);
  b[4]=0;
  for(i=0;i<n;i++)b[4]+=a[i];                      // Mean
  b[4]=b[4]/n;
  ArraySort(b);
  dcen=b[2];                                       // Distribution center
  sum2=0; sum4=0;  
  for(i=0;i<n;i++)
    {
    a[i]=a[i]-dcen;
    v=a[i]*a[i];
    sum2+=v;
    sum4+=v*v;
    }
  if(sum2<1.e-150)kurt=1.0;
  kurt=((n*n-2*n+3)*sum4/sum2/sum2-(6.0*n-9.0)/n)*(n-1.0)/(n-2.0)/(n-3.0); // Kurtosis
  if(kurt<1.0)kurt=1.0;
  gs=(1.55+0.8*MathLog10((double)n/10.0)*MathSqrt(kurt-1))*MathSqrt(sum2/(n-1));
  max=dcen+gs;
  min=dcen-gs;
  m=0;
  for(i=0;i<n;i++)if(x[i]<=max&&x[i]>=min)a[m++]=x[i];
  ArrayResize(y,m);
  ArrayCopy(y,a,0,0,m);

  if(visual==1)vis(x,dcen,min,max,n-m);
  
  return(n-m);  
  }
//----------------------------------------------------------------------------
void vis(const double &x[],double dcen,double min,double max,int numerr)
  {
  int i;
  double d,yma,ymi;
  string str;
  
  yma=x[0];ymi=x[0];
  for(i=0;i<ArraySize(x);i++)
    {
    if(yma<x[i])yma=x[i];
    if(ymi>x[i])ymi=x[i];
    }
  if(yma<max)yma=max;
  if(ymi>min)ymi=min;
  d=(yma-ymi)/20.0;
  yma+=d;ymi-=d;
  str="unset key\n";
  str+="set title 'Sequence and error levels (number of errors = "+
        (string)numerr+")' font ',10'\n";
  str+="set yrange ["+(string)ymi+":"+(string)yma+"]\n";
  str+="set xrange [0:"+(string)ArraySize(x)+"]\n";
  str+="plot "+(string)dcen+" lt rgb 'green',";
  str+=(string)min+ " lt rgb 'red',";
  str+=(string)max+ " lt rgb 'red',";
  str+="'-' with line lt rgb 'dark-blue'\n";
  for(i=0;i<ArraySize(x);i++)str+=(string)x[i]+"\n";
  str+="e\n";
  if(!saveScript(str)){Print("Create script file error");return;}
  if(!grPlot())Print("ShellExecuteW() error");
  }
//----------------------------------------------------------------------------
bool grPlot()
  {
  string pnam,param;
  
  pnam="GNUPlot\\binary\\wgnuplot.exe";
  param="-p MQL5\\Files\\gplot.txt";
  return(ShellExecuteW(NULL,"open",pnam,param,NULL,1));
  }
//----------------------------------------------------------------------------
bool saveScript(string scr1="",string scr2="")
  {
  int fhandle;
  
  fhandle=FileOpen("gplot.txt",FILE_WRITE|FILE_TXT|FILE_ANSI);
  if(fhandle==INVALID_HANDLE)return(false);
  FileWriteString(fhandle,"set terminal windows enhanced size 560,420 font 8\n");
  FileWriteString(fhandle,scr1);
  if(scr2!="")FileWriteString(fhandle,scr2);
  FileClose(fhandle);
  return(true);
  }
//----------------------------------------------------------------------------

Vamos dar uma olhada detalhada na função erremove(). Como o primeiro parâmetro da função, nós passamos o endereço do arranjo x[], onde os valores da amostragem analisada estão armazenados; o volume da amostragem não deve ser menos que quatro elementos. Supõe-se que o tamanho do arranjo x[] é igual ao tamanho da amostragem, por isso que o valor N do volume da amostragem não é transmitido. Os dados localizados no arranjo x[] não são alterados como resultado da execução da função.

O próximo parâmetro é o endereço do arranjo y[]. No caso de execução da função com sucesso, esse arranjo irá conter a sequência de entrada com valores atípicos excluídos. Tamanho do arranjo y[] é menor que o tamanho do arranjo x[] pelo número de valores excluídos da amostragem. O arranjo y[] deve ser declarado como dinâmico, caso contrário, será impossível mudar seu tamanho no corpo da função.

O último parâmetro (opcional) é a bandeira responsável pela visualização dos resultados dos cálculos. Se o valor for igual a um (valor padrão), então, antes do final da execução da função o gráfico mostrando as seguintes informações será desenhado em uma janela separada: a sequência de entrada, a linha de centro de distribuição e os limites da faixa, os valores de fora o qual será considerado como valores atípicos.

O método de desenhar gráficos será descrito depois. Em caso de execução com sucesso, a função devolve o número de valores excluídos da amostragem; em caso de erro, ela devolve -1. Se não forem descobertos valores de erros (valores atípicos), a função retornará ao 0 e a sequência do arranjo y[] será a mesma que a do x[].

No início da função, a informação é copiada do arranjo x[] para o a[], então é classificada em ordem crescente, e depois cinco estimativas do centro de distribuição são feitas.

O meio da faixa (midrange) é determinada como a soma dos valores extremos do arranjo classificado a[] dividido por dois.

A mediana é calculada por volumes ímpares de uma amostragem N como segue:

e por volumes pares da amostragem:

Considerando que os índices do arranjo classificado a[] começam do zero, nós temos:

m=(n-1)/2;
median=a[m];                                       
if((n&0x01)==0)b[1]=(median+a[m+1])/2.0;

O meio da faixa interquartil de 50% (midquartile range, MQR):

onde M=N/4 (divisão inteira).

Para o arranjo classificado a[], temos:

m=n/4;
MQR=(a[m]+a[n-m-1])/2.0;               // Midquartile range

Média aritmética de faixa interquartil de 50% (média interquartil, IQM); 25% das amostras são cortadas de ambos os lados da amostragem, e os restantes 50% são usados para cálculo da média aritmética.

onde M=N/4 (divisão inteira).

struct statParam
  {
  double mean;
  double median;
  double var;
  double stdev;
  double skew;
  double kurt;
  };
//----------------------------------------------------------------------------
int dStat(const double &x[],statParam &sP)
  {
  int i,m,n;
  double a,b,sum2,sum3,sum4,y[];
  
  ZeroMemory(sP);                                      // Reset sP
  n=ArraySize(x);
  if(n<4)                                             // Error
    {
    Print("Function dStat() error!");
    return(-1);
    }
  sP.kurt=1.0;
  ArrayResize(y,n);
  ArrayCopy(y,x);
  ArraySort(y);
  m=(n-1)/2;
  sP.median=y[m];                                     // Median
  if((n&0x01)==0)sP.median=(sP.median+y[m+1])/2.0;
  sP.mean=0;
  for(i=0;i<n;i++)sP.mean+=x[i];
  sP.mean/=n;                                         // Mean
  sum2=0;sum3=0;sum4=0;  
  for(i=0;i<n;i++)
    {
    a=x[i]-sP.mean;
    b=a*a;sum2+=b;
    b=b*a;sum3+=b;
    b=b*a;sum4+=b;
    }
  if(sum2<1.e-150)return(1);
  sP.var=sum2/(n-1);                                  // Variance
  sP.stdev=MathSqrt(sP.var);                           // Standard deviation
  sP.skew=n*sum3/(n-2)/sum2/sP.stdev;                 // Skewness
  sP.kurt=((n*n-2*n+3)*sum4/sum2/sum2-(6.0*n-9.0)/n)*
                              (n-1.0)/(n-2.0)/(n-3.0); // Kurtosis
  
  return(1);  
    
Quando o dStat() é chamado, o endereço do arranjo x[] é passado para a função. Essa contém os dados iniciais e a ligação a estrutura statParam, a qual conterá os valores calculados dos parâmetros. No caso de um erro que ocorre quando há menos de quatro elementos no arranjo, a função retorna -1.


Histograma

Em adição aos parâmetros calculados na função dStat(), a lei de distribuição da população geral é de grande interesse para nós. Para estimar visualmente a lei de distribuição na amostragem finita, nós podemos desenhar um histograma. Quando desenhado o histograma, o intervalo de valores da amostragem é dividido em várias seções semelhantes. E, então, o número de elementos em cada seção é calculado (grupos de frequências).

Além disso, um diagrama de barras é desenhado com base nos grupos de frequências. E é chamado de histograma. Após normalizar a largura da faixa, o histograma vai representar uma densidade empírica de distribuição de um valor aleatório. Vamos usar a expressão empírica descrita no "Statistics for Traders" para determinar um número otimizado de seções para desenhar o histograma:

onde L é o número de seções necessárias N é o volume da amostragem e e é o curtose.

Abaixo você pode encontrar o dHist(), que determina o número de seções, calcula o número de elementos de cada um deles e normaliza o grupo de frequências obtidas.

struct statParam
  {
  double mean;
  double median;
  double var;
  double stdev;
  double skew;
  double kurt;
  };
//----------------------------------------------------------------------------
int dHist(const double &x[],double &histo[],const statParam &sp)
  {
  int i,k,n,nbar;
  double a[],max,s,xmin;
  
  if(!ArrayIsDynamic(histo))                           // Error
    {
    Print("Function dHist() error!");
    return(-1);
    }
  n=ArraySize(x);
  if(n<4)                                             // Error
    {
    Print("Function dHist() error!");
    return(-1);
    }
  nbar=(sp.kurt+1.5)*MathPow(n,0.4)/6.0;
  if((nbar&0x01)==0)nbar--; if(nbar<5)nbar=5;          // Number of bars
  ArrayResize(a,n);
  ArrayCopy(a,x);
  max=0.0;
  for(i=0;i<n;i++)
    {
    a[i]=(a[i]-sp.mean)/sp.stdev;                     // Normalization
    if(MathAbs(a[i])>max)max=MathAbs(a[i]);
    }
  xmin=-max;
  s=2.0*max*n/nbar;
  ArrayResize(histo,nbar+2);
  ArrayInitialize(histo,0.0);
  histo[0]=0.0;histo[nbar+1]=0.0;
  for(i=0;i<n;i++)
    {
    k=(a[i]-xmin)/max/2.0*nbar;
    if(k>(nbar-1))k=nbar-1;
    histo[k+1]++;
    }
  for(i=0;i<nbar;i++)histo[i+1]/=s;
  
  return(1);
  }

O endereço do arranjo x[] é passado para a função, que contém a sequência inicial. O conteúdo do arranjo não é alterado como resultado da execução da função. Os próximos parâmetros são a ligação ao arranjo dinâmico histo[], onde os valores calculados serão armazenados. O número de elementos daquele arranjo corresponderá ao número das seções usadas para o cálculo, mais dois elementos.

Um elemento que contém o valor zero é adicionado ao início e ao fim do arranjo histo[]. O último parâmetro é o endereço da estrutura statParam que deve conter os valores previamente calculados dos parâmetros armazenados na mesma. No caso do arranjo histo[] passado para a função não ser um arranjo dinâmico ou o arranjo de entrada x[] conter menos que quatro elementos, a função para a sua execução e retorna -1.

Uma vez que você tenha desenhado um histograma dos valores obtidos, você pode visualmente estimar se a amostragem corresponde à lei normal da distribuição. Para uma representação gráfica mais visual da correspondência à lei normal da distribuição, podemos desenhar um gráfico com a escala de probabilidade normal (Diagrama de Probabilidade Normal) em adição ao histograma.


Diagrama de probabilidade normal

A ideia principal de desenhar tal gráfico é o eixo X que deverá ser estendido de modo que os valores apresentados de uma sequência com a distribuição normal fiquem na mesma linha. Desta forma, a hipótese de normalidade pode ser verificada graficamente. Você pode encontrar informações detalhadas sobre tais tipos de gráficos aqui: "Normal probability plot" ou "e-Handbook of Statistical Methods".

Para calcular os valores necessários para o desenho do gráfico de probabilidade normal, a função dRankit() mostrada abaixo é utilizada.

struct statParam
  {
  double mean;
  double median;
  double var;
  double stdev;
  double skew;
  double kurt;
  };
//----------------------------------------------------------------------------
int dRankit(const double &x[],double &resp[],double &xscale[],const statParam &sp)
  {
  int i,n;
  double np;
  
  if(!ArrayIsDynamic(resp)||!ArrayIsDynamic(xscale))    // Error
    {
    Print("Function dHist() error!");
    return(-1);
    }
  n=ArraySize(x);
  if(n<4)                                            // Error
    {
    Print("Function dHist() error!");
    return(-1);
    }
  ArrayResize(resp,n);
  ArrayCopy(resp,x);
  ArraySort(resp);
  for(i=0;i<n;i++)resp[i]=(resp[i]-sp.mean)/sp.stdev;
  ArrayResize(xscale,n);
  xscale[n-1]=MathPow(0.5,1.0/n);
  xscale[0]=1-xscale[n-1];
  np=n+0.365;
  for(i=1;i<(n-1);i++)xscale[i]=(i+1-0.3175)/np;
  for(i=0;i<n;i++)xscale[i]=ltqnorm(xscale[i]);
  
  return(1);
  }
//----------------------------------------------------------------------------

double A1 = -3.969683028665376e+01, A2 =  2.209460984245205e+02,
       A3 = -2.759285104469687e+02, A4 =  1.383577518672690e+02,
       A5 = -3.066479806614716e+01, A6 =  2.506628277459239e+00;
double B1 = -5.447609879822406e+01, B2 =  1.615858368580409e+02,
       B3 = -1.556989798598866e+02, B4 =  6.680131188771972e+01,
       B5 = -1.328068155288572e+01;
double C1 = -7.784894002430293e-03, C2 = -3.223964580411365e-01,
       C3 = -2.400758277161838e+00, C4 = -2.549732539343734e+00,
       C5 =  4.374664141464968e+00, C6 =  2.938163982698783e+00;
double D1 =  7.784695709041462e-03, D2 =  3.224671290700398e-01,
       D3 =  2.445134137142996e+00, D4 =  3.754408661907416e+00;
//----------------------------------------------------------------------------
double ltqnorm(double p)
  {
  int s=1;
  double r,x,q=0;

  if(p<=0||p>=1){Print("Function ltqnorm() error!");return(0);}
  if((p>=0.02425)&&(p<=0.97575))    // Rational approximation for central region
    {
    q=p-0.5; r=q*q;
    x=(((((A1*r+A2)*r+A3)*r+A4)*r+A5)*r+A6)*q/(((((B1*r+B2)*r+B3)*r+B4)*r+B5)*r+1);
    return(x);
    }
  if(p<0.02425)                    // Rational approximation for lower region
    {
    q=sqrt(-2*log(p)); 
    s=1;
    }
  else      //if(p>0.97575)          // Rational approximation for upper region
    {
    q = sqrt(-2*log(1-p));
    s=-1;
    }
  x=s*(((((C1*q+C2)*q+C3)*q+C4)*q+C5)*q+C6)/((((D1*q+D2)*q+D3)*q+D4)*q+1);
  return(x);
  }

O endereço do arranjo x[] é inserido na função. O arranjo contém a sequência inicial. Os próximos parâmetros são referências aos arranjos de saída resp[] e xscale[]. Após a execução da função, os valores utilizados para desenhar o gráfico nos eixos X e Y, respectivamente, são escritos nos arranjos. Então, o endereço da estrutura statParam é passado para a função. Essa deve conter os valores previamente calculados dos parâmetros estatísticos da sequência de entrada. No caso de um erro, a função retorna a -1.

Quando formar valores para o eixo X, a função ltqnorm() é chamada. Calcula a função integral inversa da distribuição normal. O algoritmo que é usado para o cálculo foi pego do "An algorithm for computing the inverse normal cumulative distribution function".


Quatro gráficos

Anteriormente eu mencionei a função dStat() onde os valores dos parâmetros estatísticos são calculados. Vamos repetir brevemente o seu significado.

Dispersão (variação) – o valor médio dos quadrados de desvio de um valor aleatório da sua expectativa matemática (valor médio). O parâmetro que mostra quão grande é o desvio de um valor aleatório a partir do seu centro de distribuição. Quanto maior o valor deste parâmetro é, maior é o desvio.

Desvio padrão – uma vez que a dispersão é medida como o quadrado de um valor aleatório, o desvio padrão é frequentemente utilizado como uma das características mais evidentes de dispersão. Ela é igual à raiz quadrada da dispersão.

Assimetria – se desenharmos uma curva de distribuição de um valor aleatório, a assimetria irá mostrar o quão assimétrica é a curva de densidade de probabilidade relativamente ao centro de distribuição. Se o valor de assimetria é maior do que zero, a curva de densidade de probabilidade tem um declive íngreme à esquerda e à direita um declive plano. Se o valor da assimetria é negativo, então a inclinação à esquerda é plana e a da direita é íngreme. Quando a curva da densidade de probabilidade é simétrica ao centro da distribuição, a assimetria é igual a zero.

O coeficiente de excesso (curtose) – descreve a agudez de um pico da curva de densidade de probabilidade e a inclinação de declives das caudas de distribuição. Quanto mais acentuado é o pico da curva perto do centro de distribuição, maior é o valor de curtose.

Apesar do fato que os referidos parâmetros estatísticos descrevem um sequência em detalhes, muitas vezes você pode caracterizar uma sequência de uma maneira mais fácil - com base no resultado de estimativas representadas em forma de gráfico. Por exemplo, um gráfico comum de uma sequência pode completar muito uma visão obtida ao analisar os parâmetros estatísticos.

Anteriormente no artigo, eu mencionei as funções dHist() e dRankit() que permitem preparar dados para desenhar um histograma ou uma tabela com a escala da probabilidade normal. A exibição do histograma e do gráfico de distribuição normal junto com o gráfico comum na mesma folha, permite determinar as principais características da sequência analisada visualmente.

Os três gráficos listados devem ser complementados com outro - o gráfico com os valores atuais da sequência no eixo Y e seus valores anteriores no eixo X. Tal gráfico é chamado de "Lag Plot". Se existe uma forte correlação entre indicações adjacentes, os valores de uma amostragem vão se estender em uma linha reta. E se não existir uma correlação entre indicações adjacentes, por exemplo, quando se analisar uma sequência aleatória, então os valores serão dispersos por todo o gráfico

Para uma estimativa rápida de uma amostragem inicial, sugiro desenhar quatro gráficos em uma única folha e exibir os valores calculados do parâmetro estatístico neles. Essa não é uma nova ideia; você pode ler sobre o uso de análises de quatro gráficos mencionados aqui: "4-Plot".

No final do artigo, tem a seção de "Arquivos" contendo o roteiro s4plot.mq5, o qual desenha esses quatros gráficos em uma única folha. O arranjo dat[] é criado dentro da função OnStart() do roteiro. E essa contém a sequência inicial. Então as funções dStat(), dHist() e dRankit() são chamadas, consequentemente para o cálculo dos dados necessários para o desenho dos gráficos. A função vis4plot() é chamada a seguir. Ela cria um arquivo de texto com os comandos gnuplot com base nos dados calculados e, em seguida, chama o aplicativo para desenhar os gráficos numa janela separada.

Não há o porque mostrar todo o código do roteiro em artigo, já que dStat(), dHist() e dRankit() foram descritas anteriormente, a função vis4plot(), que cria uma sequência de comandos gnuplot, não possui peculiaridades importantes, e descrição dos comandos gnuplot sai dos limites do assunto do artigo. Em adição a isso, você pode usar outro método para desenhar os gráficos ao invés de usar o aplicativo gnuplot.

Então vamos mostrar somente uma parte da s4plot.mq5 - sua função OnStart().

//----------------------------------------------------------------------------
// Script program start function
//----------------------------------------------------------------------------
void OnStart()
  {
  int i;
  double dat[128],histo[],rankit[],xrankit[];
  statParam sp;
 
  MathSrand(1);
  for(i=0;i<ArraySize(dat);i++) dat[i]=MathRand();
  
  if(dStat(dat,sp)==-1)return;
  if(dHist(dat,histo,sp)==-1)return;
  if(dRankit(dat,rankit,xrankit,sp)==-1)return;
  
  vis4plot(dat,histo,rankit,xrankit,sp,6);
  }

Nesse exemplo, uma sequência aleatória é usada para preencher o arranjo dat[] com dados iniciais usando a função MathRand(). A execução do roteiro deve resultar a seguir:

4-Plot

Figura 3. Quatro gráficos. Roteiro s4plot.mq5

Você deve prestar atenção ao último parâmetro da função vis4plot(). Ela é responsável pelo formato de valores numéricos de saída. Nesse exemplo, os valores são retornados com seis casas decimais. Esse parâmetro é o mesmo que aquele que determina o formato na função DoubleToString().

Se os valores da sequência de entrada são muito pequenos ou muito grandes, você pode usar o formato científico para uma exibição mais óbvia. Para fazer isso, ajuste esse parâmetro para -5, por exemplo. O valor -5 é ajustado no padrão para a função vis4plot().

Para demonstrar a evidência do método de quatro gráficos para exibição de peculiaridades de uma sequência, vamos precisar de um gerador de tais sequências.


Gerador de uma sequência pseudo-aleatória

A classe RNDXor128 se destina a gerar sequências pseudo-aleatórias.

Abaixo, encontra-se o código-fonte do arquivo de inclusão descrevendo essa classe.

//-----------------------------------------------------------------------------------
//                                                                      RNDXor128.mqh
//                                                                      2011, victorg
//                                                                https://www.mql5.com
//-----------------------------------------------------------------------------------
#property copyright "2011, victorg"
#property link      "https://www.mql5.com"

#include <Object.mqh>
//-----------------------------------------------------------------------------------
// Generation of pseudo-random sequences. The Xorshift RNG algorithm 
// (George Marsaglia) with the 2**128 period of initial sequence is used.
//          uint rand_xor128()
//            {
//            static uint x=123456789,y=362436069,z=521288629,w=88675123;
//            uint t=(x^(x<<11));x=y;y=z;z=w;
//            return(w=(w^(w>>19))^(t^(t>>8)));
//            }
// Methods:
//  Rand()      - even distribution withing the range [0,UINT_MAX=4294967295].
//  Rand_01()   - even distribution within the range [0,1].
//  Rand_Norm() - normal distribution with zero mean and dispersion one.
//  Rand_Exp()  - exponential distribution with the parameter 1.0.
//  Rand_Laplace() - Laplace distribution with the parameter 1.0
//  Reset()     - resetting of all basic values to initial state.
//  SRand()     - setting new basic values of the generator.
//-----------------------------------------------------------------------------------
#define xor32  xx=xx^(xx<<13);xx=xx^(xx>>17);xx=xx^(xx<<5)
#define xor128 t=(x^(x<<11));x=y;y=z;z=w;w=(w^(w>>19))^(t^(t>>8))
#define inidat x=123456789;y=362436069;z=521288629;w=88675123;xx=2463534242

class RNDXor128:public CObject
  {
protected:
  uint      x,y,z,w,xx,t;
  uint      UINT_half;
public:
            RNDXor128()       {UINT_half=UINT_MAX>>1;inidat;};
  double    Rand()            {xor128;return((double)w);};
  int       Rand(double& a[],int n)
                              {int i;if(n<1)return(-1);
                               if(ArraySize(a)<n)return(-2);
                               for(i=0;i<n;i++){xor128;a[i]=(double)w;}
                               return(0);};
  double    Rand_01()         {xor128;return((double)w/UINT_MAX);};
  int       Rand_01(double& a[],int n)
                              {int i;if(n<1)return(-1);
                               if(ArraySize(a)<n)return(-2);
                               for(i=0;i<n;i++){xor128;a[i]=(double)w/UINT_MAX;}
                               return(0);};
  double    Rand_Norm()       {double v1,v2,s,sln;static double ra;static uint b=0;
                               if(b==w){b=0;return(ra);}
                               do{
                                 xor128;v1=(double)w/UINT_half-1.0;
                                 xor128;v2=(double)w/UINT_half-1.0;
                                 s=v1*v1+v2*v2;
                                 }
                               while(s>=1.0||s==0.0);
                               sln=MathLog(s);sln=MathSqrt((-sln-sln)/s);
                               ra=v2*sln;b=w;
                               return(v1*sln);};
  int       Rand_Norm(double& a[],int n)
                              {int i;if(n<1)return(-1);
                               if(ArraySize(a)<n)return(-2);
                               for(i=0;i<n;i++)a[i]=Rand_Norm();
                               return(0);};
  double    Rand_Exp()        {xor128;if(w==0)return(DBL_MAX);
                               return(-MathLog((double)w/UINT_MAX));};
  int       Rand_Exp(double& a[],int n)
                              {int i;if(n<1)return(-1);
                               if(ArraySize(a)<n)return(-2);
                               for(i=0;i<n;i++)a[i]=Rand_Exp();
                               return(0);};
  double    Rand_Laplace()    {double a;xor128;
                              a=(double)w/UINT_half;
                              if(w>UINT_half)
                                {a=2.0-a;
                                if(a==0.0)return(-DBL_MAX);
                                return(MathLog(a));}
                              else
                                {if(a==0.0)return(DBL_MAX);
                                return(-MathLog(a));}};
  int       Rand_Laplace(double& a[],int n)
                              {int i;if(n<1)return(-1);
                               if(ArraySize(a)<n)return(-2);
                               for(i=0;i<n;i++)a[i]=Rand_Laplace();
                               return(0);};
  void      Reset()           {inidat;};
  void      SRand(uint seed)  {int i;if(seed!=0)xx=seed;
                               for(i=0;i<16;i++){xor32;}
                               xor32;x=xx;xor32;y=xx;
                               xor32;z=xx;xor32;w=xx;
                               for(i=0;i<16;i++){xor128;}};
  int       SRand(uint xs,uint ys,uint zs,uint ws)
                              {int i;if(xs==0&&ys==0&&zs==0&&ws==0)return(-1);
                               x=xs;y=ys;z=zs;w=ws;
                               for(i=0;i<16;i++){xor128;}
                               return(0);};
  };
//-----------------------------------------------------------------------------------

O algoritmo usado para gerar uma sequência aleatória é descrito em detalhes no artigo "Xorshift RNGs" de George Marsaglia (olhe o xorshift.zip no final do artigo). Métodos da classe RNDXor128 são descritos no arquivo RNDXor128.mqh. Usando essa classe, você pode obter sequências com distribuição par, normal ou exponencial ou com a distribuição Laplace (exponencial dupla).

Você deve prestar atenção para o fato que quando uma instância da classe RNDXor128 é criada, os valores básicos da seqüência são ajustados para estado inicial. Assim, em contraste com a chamada da função MathRand() a cada novo início de um roteiro ou indicador que usa RNDXor128, uma e a mesma sequência será gerada. O mesmo que quando chamar MathSrand() e, em seguida, MathRand().


Exemplos de sequência

Abaixo, como um exemplo, é possível encontrar os resultados obtidos quando se analisa sequências que são extremamente diferentes umas das outras em relação as suas propriedades.

Exemplo 1. Uma sequência aleatória com a Lei Par de Distribuição.

#include "RNDXor128.mqh"
RNDXor128 Rnd;
//----------------------------------------------------------------------------
void OnStart()
  {
  int i;
  double dat[512];

  for(i=0;i<ArraySize(dat);i++) dat[i]=Rnd.Rand_01();
  ...  
  }

Distribuição par

Figura 4. Distribuição par

Exemplo 2. Uma sequência aleatória com a Lei Normal da Distribuição.

#include "RNDXor128.mqh"
RNDXor128 Rnd;
//----------------------------------------------------------------------------
void OnStart()
  {
  int i;
  double dat[512];

  for(i=0;i<ArraySize(dat);i++) dat[i]=Rnd.Rand_Norm();
  ...  
  }

Distribuição normal

Figura 5. Distribuição normal

Exemplo 3. Uma sequência aleatória com a Lei Exponencial da Distribuição.

#include "RNDXor128.mqh"
RNDXor128 Rnd;
//----------------------------------------------------------------------------
void OnStart()
  {
  int i;
  double dat[512];

  for(i=0;i<ArraySize(dat);i++) dat[i]=Rnd.Rand_Exp();
  ...  
  }

Distribuição Exponencial

Figura 6. Distribuição Exponencial

Exemplo 4. Uma sequência aleatória com Distribuição Laplace.

#include "RNDXor128.mqh"
RNDXor128 Rnd;
//----------------------------------------------------------------------------
void OnStart()
  {
  int i;
  double dat[512];

  for(i=0;i<ArraySize(dat);i++) dat[i]=Rnd.Rand_Laplace();
  ...  
  }

Distribuição Laplace

Figura 7. Distribuição Laplace

Exemplo 5. Sequência sinusoidal

//----------------------------------------------------------------------------
void OnStart()
  {
  int i;
  double dat[512];

  for(i=0;i<ArraySize(dat);i++) dat[i]=MathSin(2*M_PI/4.37*i);
  ...  
  }

Sequência Sinusoidal

Figura 8. Sequência Sinusoidal

Exemplo 6. Uma sequência com correlação visível entre indicações adjacentes.

#include "RNDXor128.mqh"
RNDXor128 Rnd;
//----------------------------------------------------------------------------
void OnStart()
  {
  int i;
  double dat[512],a;

  for(i=0;i<ArraySize(dat);i++) {a+=Rnd.Rand_Laplace();dat[i]=a;}
  ...  
  }

Correlação entre as indicações adjacentes.

Figura 9. Correlação entre as indicações adjacentes.


Conclusão

O desenvolvimento de algoritmos de programas que implementam qualquer tipo de cálculos é sempre um trabalho difícil de fazer. O motivo é a necessidade de considerar uma série de requisitos para minimizar erros que podem surgir ao arredondar, truncar e extravasar variáveis.

Enquanto escrevia os exemplos para o artigo, eu não realizei qualquer análise de algoritmos do programa. Ao escrever a função, os algoritmos matemáticos foram implementados "diretamente. Assim, se você for usá-los em aplicações "sérias", você deve analisar a sua estabilidade e precisão.

O artigo não descreve característica do aplicativo gnuplot de modo algum. Essas questões vão além da abrangência deste artigo. Enfim, eu gostaria de mencionar que o gnuplot pode ser adaptado para ser usado em conjunto com o MetaTrader 5. Para isso, você precisa fazer algumas correções no seu código-fonte e recompilá-lo. Em adição a isso, a forma de passar os comandos para gnuplot usando um arquivo provavelmente não é o melhor caminho, já que a interação com gnuplot pode ser organizada através de uma interface de programação.


Arquivos

  • erremove.mq5 – exemplo de um roteiro que exclui erros de uma amostragem.
  • function_dstat.mq5 – função para calcular parâmetros estatísticos.
  • function_dhist.mq5 - função para cálculo de valores do histograma.
  • function_drankit.mq5 – função para cálculo de valores usados quando se desenha um gráfico com a escala da distribuição normal.
  • s4plot.mq5 – exemplo de roteiro que desenha quatro gráficos em uma única folha.
  • RNDXor128.mqh – classe do gerador de uma sequência aleatória.
  • xorshift.zip - George Marsaglia. "Xorshift RNGs".

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

Arquivos anexados |
erremove.mq5 (4.53 KB)
function_dhist.mq5 (1.21 KB)
function_dstat.mq5 (1.35 KB)
rndxor128.mqh (12.01 KB)
s4plot.mq5 (10.52 KB)
xorshift.zip (38.35 KB)
Usar Mapas Auto-organizáveis (mapas de Kohonen) no MetaTrader 5 Usar Mapas Auto-organizáveis (mapas de Kohonen) no MetaTrader 5
Um dos aspectos mais interessantes dos Mapas auto-organizáveis (mapas de Kohonem) é que eles aprendem a classificar os dados sem supervisão. Em seu formato básico, ele produz um mapa de similaridade dos dados de entrada (clustering). Os mapas SOM podem ser usados para a classificação e visualização de dados de alta dimensão. Neste artigo, serão apresentadas diversas aplicações simples dos mapas de Kohonen.
Pagamentos e métodos de pagamento Pagamentos e métodos de pagamento
A MQL5.community oferece amplas oportunidades de ganhar aos traders e desenvolvedores de aplicativos de negociação para o terminal MetaTrader. Neste artigo, explicaremos como o pagamento de serviços MQL5 e a retirada de fundos acorrem, também veremos como é mantida a segurança ao realizar operações.
Criar critérios personalizados de otimização de Expert Advisors Criar critérios personalizados de otimização de Expert Advisors
O terminal do cliente MetaTrader 5 oferece uma ampla gama de possibilidades de otimização dos parâmetros de Expert Advisor. Além dos critérios de otimização inclusos no provador de estratégia, os desenvolvedores têm a possibilidade de criar os seus próprios critérios. Isto leva a um número quase ilimitado de possibilidades de teste e otimização dos Expert Advisors. Este artigo descreve formas práticas, tanto simples como complexas, de criação desses critérios.
Rastreamento, Depuração e Análise Estrutural de Código Fonte Rastreamento, Depuração e Análise Estrutural de Código Fonte
O complexo inteiro de problemas de criação de uma estrutura de um código executado e seu rastreamento pode ser resolvido sem muitas dificuldades. Esta possibilidade apareceu no MetaTrader 5 devido a um novo recurso da linguagem MQL5 - criação automática de variáveis de tipo complexo de dados (estruturas e classes) e sua eliminação quando fora do escopo local. O artigo contém a descrição da metodologia e a ferramenta pronta para uso.