外汇EA编写教程:统计估计

简介

现在,你经常看到与计量经济学、价格序列预测、选择和评估模型的适当性相关的文章和出版物。然而,在大多数情况下,推理是基于这样一个假设:读者精通数理统计,并且可以很容易地估计分析序列的统计参数。

估计序列的统计参数非常重要,因为大多数数学模型和方法都是基于不同的假设。例如,正态分布或偏差(或其他参数)就是这样。因此,在分析和预测时间序列时,我们需要一个简单方便的工具来快速、清晰地估计主要统计参数。在本文中,我们将尝试创建这样的工具。

本文简要介绍了随机序列最简单的统计参数及其可视化分析的几种方法。本文还介绍了如何在MQL5中实现这些方法,以及如何使用gnuplot应用程序可视化计算结果。本条无意用作手册或参考资料,因此可能包含一些公认的术语和定义。

样本参数分析

假设在时间上存在一个无限的静态过程,可以用一个离散的样本序列来表示。我们称之为样本序列总体。从总体中选择的一部分样本称为总体抽样或n个样本抽样。此外,假设我们不知道任何参数的真实值,我们将根据有限采样法进行估计。

避免离群值

在开始参数的统计估计之前,我们应该注意,如果采样误差(离群值)太大,估计的准确性可能不够。如果采样量太小,离群值对估计精度的影响较大。异常值是指偏离配送中心的异常值。这种偏差可能是由于统计数据收集和序列形成过程中的不同罕见事件和错误造成的。

很难决定是否过滤异常值,因为在大多数情况下,不清楚值是异常值还是属于被分析的流程。所以,如果我们检测出异常值并决定过滤掉它们,就会有一个问题——我们如何处理这些错误?最合乎逻辑的方法是从抽样中排除它们,这可以提高统计特征的估计精度;但是,在处理时间序列时,应该小心地从抽样中排除异常值。

为了能够从抽样中排除异常值,或者至少检测到异常值,让我们实现S.V.Bulashev’sBookStatisticsforTraders中描述的算法。

根据该算法,我们需要计算配送中心的五个估计值:

  1. 中值的;
  2. 50%四分位中心距(中四分位距离,mqr);
  3. 整个样本的算术平均值;
  4. 50%四分位距离的算术平均值(平均四分位距离,iqm);
  5. 间距中心(中间列数)-确定采样中最大值和最小值的平均值。

接下来,将分布中心的估计结果按升序排列,然后选择平均值(序列中的第三个值)作为分布中心xcen。因此,选择的估计值似乎受异常值的影响最小。

此外,标准方差S、过剩系数K和删除系数的值可通过获得的分布中心估计xcen和以下经验公式计算:

和NBSP;

其中n为采样中的样本数。

接下来,它超出了以下范围:

该值将被视为异常值,因此应从样本中排除。

这个方法在交易者统计中有详细的描述,所以让我们直接开始算法的实现。允许检测和消除异常值的算法在erremove()函数中实现。

您可以在以下部分中找到为测试此函数而编写的脚本。

//----------------------------------------------------------------------------
//                                                                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);
  }
//----------------------------------------------------------------------------

让我们详细看看erremove()函数。作为函数的第一个参数,我们传递数组x[]的地址,并在数组中存储分析样本的值;样本大小不应小于四个元素。假设数组x[]的大小等于示例的大小,这就是为什么不传递示例的n值的原因。执行函数不会更改存储在数组x[]中的数据。

下一个参数是数组y[]的地址。如果函数成功执行,数组将在排除异常值后包含输入序列。数组y[]的大小小于数组x[]的大小,其差等于从示例中排除的值的数目。数组y[]必须声明为动态数组,否则不能在函数体中更改其大小。

最后一个(可选)参数是负责可视化计算结果的符号。如果其值等于1(默认值),则在执行函数之前,将在单独的窗口中显示包含以下信息的图表:输入序列、分布中心线和范围限制,超出该范围的值被视为异常值。

稍后将介绍绘制图形的方法。如果执行成功,函数将返回从样本中排除的值的数目;如果发生错误,则返回-1。如果未找到较大的偏差(离群值),则函数返回0,数组y[]中的序列将与x[]中的序列相同。

在函数开始时,将信息从数组x[]复制到数组a[],按升序排序,然后计算分布中心的五个估计值。

间隔的中心(中间列的数目)等于排序数组a[]的极值除以2的和。

中间值的计算分为两类。对于奇数采样n,使用以下公式:

和NBSP;

对于均匀采样,使用以下公式:

和NBSP;

考虑到排序数组a[]的索引从0开始,我们得到:

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

50%四分位数中心间距(中间四分位数力矩,mqr):

m=n/4(除法)。

对于排序后的数组A[],我们得到:

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

50%四分位距离(平均四分位距离,iqm)的算术平均值。25%的样本从两个极端值中移除,剩下的50%用于计算算术平均值。

和NBSP;

m=n/4(除法)。

m=n/4; IQM=0;
for(i=m;i<n-m;i++)IQM+=a[i];
IQM=IQM/(n-2*m);                       // Interquartile mean(IQM)

算术平均值(平均值)由整个采样确定。

将每个确定的值写入b[]数组,然后按升序排列。选择数组中元素b[2]的值作为分发中心。此外,我们将使用该值计算算术平均值和冗余系数的无偏估计,并将在未来解释该算法。

接下来,使用估计值计算异常值的删除系数和范围限制(表达式如上所示)。最后,在数组y[]中生成一个排除异常值的序列,并调用vis()函数绘制图形。让我们简单看看本文中使用的可视化方法。

可视化

为了显示结果,我使用了免费的应用程序gnuplot,它用于绘制各种二维和三维图形。gnuplot可以在屏幕上(在单独的窗口中)显示图表,也可以将其以不同的图形格式写入文件。可以从准备好的文本文件执行绘图命令。gnuplot项目的官方网站是gnuplot。SooCeFig公司NET。应用程序支持多个平台,并作为源代码文件和专门为特定平台编译的二进制文件发布。

在Windowsxpsp3中,gnuplot 4.2.2版用于测试专门为本文编写的示例。gp442win32.zip文件可从https://sourceforge.net/projects/gnuplot/files/gnuplot/4.4.2/下载。我没有用其他版本的gnuplot测试这些示例。

下载gp442win32.zip文件后,请将其解压缩。这将创建/gnuplot文件夹,其中包含应用程序、帮助文件、说明文档和示例。要与应用程序交互,请将整个/gnuplot文件夹放在metatrader 5客户机的根文件夹中。

/gnuplot和NBSP;

&图1。放置文件夹/gnuplot

移动文件夹后,可以更改gnuplot应用程序的可操作性。为此,请执行文件/gnuplot/binary/wgnuplot。exe,然后在出现“gnuplot&gt;”命令提示时键入“plot sin(x)”命令。因此,将有一个窗口在其中绘制sin(x)函数。您还可以尝试应用程序交付包中包含的示例;为此,请选择“文件/演示”菜单项,然后选择文件/gnuplot/demo/all.dem。

现在,当您启动erremove.mq5脚本时,图2中所示的图将在单独的窗口中绘制:

erremove.mq5和NBSP;

图2。使用erremove.mq5脚本绘制的图形。

此外,在本文中,我们很少讨论gnuplot用法的一些特性,因为您可以在附带的文档和各种网站(如https://gnuplot.ikir.ru/)中轻松找到有关程序及其控制的信息。

本文编写的程序示例使用与gnuplot交互的最简单方法来绘制图形。首先,创建文本文件gplot。txt,包含gnuplot命令和要显示的信息。接下来,启动wgnuplot.exe应用程序,并在命令行中将文件名作为参数传递。使用从系统库文件shell32.dll导入的shellExecutew()函数调用wgnuplot.exe应用程序;这就是为什么必须允许在客户端导入外部dll文件的原因。

提供的gnuplot版本允许在一个窗口中为两种类型的终端(wxt和windows)绘制图表:wxt终端使用抗混叠算法绘制图表,并且可以获得比窗口终端更高质量的图片。但是,在编写这个示例时使用了窗口终端。原因是,当使用窗口终端时,通过调用“wgnuplot.exe-p mql5//files//gplot.txt”并打开图形窗口创建的系统进程将在窗口关闭时自动关闭。

如果选择Wxt终端,则当图表窗口关闭时,系统进程wgnuplot.exe不会自动关闭。因此,如果您使用wxt客户机并如上所述多次调用wgnuplot.exe,则可能会累积多个进程,而系统中没有任何活动迹象。使用“wgnuplot.exe-p mql5//files//gplot.txt”调用和窗口终端可以避免打开不必要的其他窗口和出现打开的系统进程。

显示图表的窗口是交互式的,可以处理鼠标单击事件和键盘事件。要获取有关默认热键的信息,请运行wgnuplot.exe,使用“设置终端窗口”命令选择终端类型,然后绘制任何图表,例如使用“plot sin(x)”命令。如果图表窗口处于活动状态(突出显示),则可以通过按“H”键在wgnuplot.exe的文本窗口中看到提示。

参数
估算

在快速了解了图表方法之后,让我们回到基于人口有限抽样的总参数估计。假设我们不知道人口的统计参数,我们将只使用这些参数的无偏估计。

数学期望的估计或抽样均值可以作为确定序列分布的主要参数。使用以下公式计算采样平均值:

和NBSP;

n是样本中的样本数。

平均值是对配送中心的估计,用于计算与中心移动相关的其他参数,这使得该参数特别重要。除平均值外,我们还使用偏差(偏差、方差)、标准方差、偏态系数(偏态)和过剩系数(峰度)作为统计参数。

0&NBSP;

其中,M是中心差异。

中心差分是整个分布的数字特征。

第二、第三和第四选择中心差分由以下表达式确定:

1和NBSP;

但这些价值观是没有偏见的。这里,我们应该提到K统计和H统计。在某些情况下,它们可以允许对中心动量差进行无偏估计,因此可以用来计算偏差、标准方差、偏态和峰度的无偏估计。

注意,在k和h估计中,第四个微分是以不同的方式计算的。因此,当用k或h来评价峰度时,得到了不同的表达式。例如,在Microsoft Excel中,使用k估计对应的公式计算超额,而在交易员统计中,使用h估计执行无偏峰度估计。

让我们在前面的表达式中选择h估计并用“m”替换它来计算所需的参数。

偏差和标准偏差:

2&NBSP;

Skewness:

3&NBSP;

Kurtosis:

4&NBSP;

为了服从正态分布规律,给出了该表达式,由该表达式计算出的过剩系数(峰度)等于3。

需要注意的是,从计算值中减去3得到的值通常用作峰度值;因此,相对于正态分布规律,获得的值是标准化的。在第一种情况下,这个系数被称为峰度;在第二种情况下,它被称为“超越峰度”。

在dstat()函数中,参数根据给定表达式计算:

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);  

在调用 dStat() 时,数组 x[] 的地址被传递给函数。它包含初始数据以及到 statParam 结构的链接,该结构将包含计算出来的参数值。当发生数组少于四个元素的错误时,函数会返回 -1。

直方图

除了DSTAT()函数中计算的参数外,我们还对一般分布律感兴趣。为了直观地估计有限样本的分布,我们可以绘制一个直方图。在绘制柱状图时,采样值的范围被划分为几个类似的段。然后计算每个段落中的元素数(组频率)。

此外,还可以根据组频率绘制柱状图。它被称为柱状图。在归一化到范围宽度后,直方图将代表随机值的经验分布密度。让我们使用统计中为交易者引入的经验表达式来确定绘制直方图的最佳分段数:

5&NBSP;

其中,L为所需段数,N为采样量,E为峰度。

下面是dhist()函数,它用于确定段数,计算每个段中的元素数,并标准化获得的组频率。

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);
  }

数组x[]的地址被传递到复合函数中。它包含初始序列。函数的执行不会更改数组的内容。下一个参数用作动态数组histo[]的链接,其中存储计算值。数组中的元素数将等于用于计算的段数加上两个元素。

在数组histo[]的开头和结尾添加一个包含零的元素。最后一个参数是statParam结构的地址,它应该包含以前计算并存储在其中的参数值。如果传递给函数的数组histor[]不是动态数组,或者输入数组x[]包含的元素少于四个,则该函数将停止执行并返回-1。

一旦绘制出所得值的直方图,就可以直观地估计采样是否符合正态分布规律。为了得到一个更直观的正态分布规律是否服从的图,除了直方图外,我们还可以绘制一个正态概率尺度的图(正态概率图)。

正态概率图

绘制这些图的主要目的是拉伸X轴,使服从正态分布的序列的显示值位于同一条线上。这样,就可以用图形来检查正常假设。您可以通过以下链接找到关于这些图形的详细信息:“正态概率图”或“统计方法电子手册”。

为了计算绘制正态概率图所需的值,使用以下函数drankit()。

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);
  }

数组x[]的地址被输入到函数中。数组包含初始序列。下一个参数是对输出数组resp[]和xscale[]的引用。执行函数后,用于在X轴和Y轴上绘制图形的值分别写入数组。然后将statParam结构的地址传递给函数。它应该包含先前计算的输入序列的统计参数值。如果出现问题,函数返回-1。

为X轴生成值时,应调用函数ltqnorm()。用于正态分布反积分函数的计算。该算法来源于“反正态累积分布函数的计算算法”(反正态累积分布函数的计算算法)。

四张图表

我前面提到了dstat()函数,在该函数中可以计算统计参数的值。让我们简要回顾一下它们的含义。

偏差(方差)-随机值与其数学期望值(平均值)之间偏差的均方。此参数显示随机值与其分布中心之间的偏差。这个参数的值越大,偏差就越大。

标准方差-因为偏差是由一个随机值的平方来计算的,所以标准方差通常被用作一个更明显的偏差特征。它等于偏差的平方根。

偏度-如果我们绘制随机值的分布曲线,偏度将显示概率密度曲线和分布中心之间的不对称程度。当偏度大于零时,概率密度曲线的左斜率更陡,右斜率更平衡。如果倾斜度为负,则左坡平缓,右坡陡。当概率分布曲线与分布中心对称时,偏态等于零。

过剩系数(峰度)-它描述概率密度曲线峰值的锐度和分布尾坡的陡度。分布中心附近的曲线峰值越尖锐,峰值越大。

尽管上面提到的统计参数详细描述了一个序列,但通常可以基于以图形方式表示的估计结果以更简单的方式描述序列的特性。例如,在分析统计参数时,公共序列图可以证实观点。

在本文的前面,我提到了dhist()和drankit()函数,它们允许为具有正态概率尺度的柱状图或图形准备数据。通过在同一张纸上显示柱状图、正态分布图和一般图形,可以直观地确定分析序列的主要特征。

所列三个图表应补充另一个图表,其中序列的当前值由Y轴表示,序列的先前值由X轴表示。这种图称为“滞后图”。如果相邻指标之间有很强的相关性,则将采样值画成直线。如果相邻指标之间没有相关性,例如在分析随机序列时,这些值将分散在整个图上。

为了快速估计初始采样,建议在一张图上绘制四个图表,并显示计算出的统计参数。这个想法并不新鲜;您可以通过以下链接阅读关于使用上述四个图表进行分析的文章“4-plot”。

本文末尾的“文件”部分包含脚本s4plot。mq5,可用于在单个绘图上绘制这四个图表。数组dat[]是在脚本的onStart()函数中创建的。数组包含初始序列。然后依次调用dstat()、dhist()和drankit()函数来计算绘制图表所需的数据。接下来,调用vis4plot()函数。此函数基于计算数据创建包含gnuplot命令的文本文件,然后调用应用程序在单独的窗口中绘制图形。

在本文中显示整个脚本代码是毫无意义的,因为之前已经解释过dstat()、dhist()和drankit()函数,并且创建gnuplot命令序列的vis4plot()函数没有显著的特性,gnuplot命令的描述超出了本文的范围。此外,您可以用其他方式绘制图表,而无需使用gnuplot应用程序。

所以我们只展示S4Plot的一部分。mq5—它的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);
  }

在本例中,使用mathrand()函数,并使用随机序列将初始数据填充到dat[]数组中。执行脚本后,应返回以下结果:

6&NBSP;

图3。四张图表。脚本s4plot.mq5

您应该注意vis4plot()函数的最后一个参数。它负责输出值的格式。在这种情况下,输出值有六个十进制数字。此参数与DoubleToString()函数中用于确定格式的参数相同。

如果输入序列的值太小或太大,可以使用科学的计数来实现更清晰的显示。例如,为了实现这个目标,参数可以设置为-5。值-5是vis4plot()功能的默认设置。

为了清楚地说明用四个图表显示序列特征的方法,我们需要使用这种序列的生成器。

伪随机序列发生器

类RNDXOR128用于生成伪随机序列。

以下是描述此类包含文件的源代码。

//-----------------------------------------------------------------------------------
//                                                                      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);};
  };
//-----------------------------------------------------------------------------------

在George Marsaglia的“xorshift-rngs”中,详细描述了生成伪随机序列的算法(请参见本文末尾的xorshift.zip)。rndxor128类的方法在文件rndxor128.mqh中描述。利用这个类,可以得到一个遵循均匀分布、正态分布、经验分布或拉普拉斯分布(双指数分布)规律的序列。

您应该注意,当您创建rndxor128类的实例时,序列的基值被设置为初始状态。因此,与每次启动使用rndxor128的脚本或指示器时调用mathrand()函数相比,生成相同的序列。调用mathsrand()和mathrand()时也是如此。

序列示例

例如,在分析具有非常不同属性的序列时,可以找到下面的结果。

例1。服从均匀分布规律的随机序列。

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

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

7

图4。均匀分布

例2。服从正态分布规律的随机序列。

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

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

8

图5。正态分布

例3。服从指数分布的随机序列。

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

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

9

图6。指数分布

例4。服从拉普拉斯分布的随机序列。

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

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

0

图7。拉普拉斯分布

例5。正弦序列

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

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

1

图8。正弦序列

例6。相邻指标相关性明显的序列。

#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;}
  ...  
  }

2

图9。相邻指令之间的相关性

总结

开发能够实现任何类型计算的程序算法始终是一项困难的任务。原因是必须考虑大量的需求,以尽量减少舍入、截断和溢出变量中可能出现的错误。

在编写本文中的示例时,我没有对程序算法进行任何分析。在编写函数时,数学算法是“直接”实现的。因此,如果您想在重要的应用程序中使用它们,应该分析它们的稳定性和准确性。

本文完全没有描述gnuplot应用程序的特性。这些问题不属于本文的范围。无论如何,我想指出gnuplot可以修改为与metatrader 5一起工作。为此,您需要对其源代码进行一些更改并重新编译它。此外,使用文件将命令传递给gnuplot可能不是最佳实践,因为与gnuplot的交互可以通过编程接口实现。

文件

  • Erremove。mq5—一个从采样中排除错误的脚本示例。
  • 函数“dstat”。mq5——用于计算统计参数的函数。
  • 函数_dhist.mq5-用于计算柱状图值的函数。
  • 函数_drankit.mq5——用于计算正态分布比例图中使用的值的函数。
  • S4-图。一个脚本示例,用于在一个绘图上绘制四个图表。
  • rndxor128.mqh-随机序列生成器类。
  • “xorshift-rngs”作者xorshift.zip-george marsaglia。

本文由MetaQuotes Software Corp.翻译自俄语原文
,网址为https://www.mql5.com/ru/articles/273。

附加文件下载zip erremove.mq5(4.53 kb)函数_dhist.mq5(1.21 kb)函数_drankit.mq5(2.46 kb)xorshift.zip(38.35 kb)函数_dstat.mq5(1.35 kb)s4plot.mq5(10.52 kb)rndxor128.mqh(12.01 kb)

 

 


MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!

 

免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经(www.myfxtop.cn)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。

風險提示

MyFxtops邁投所列信息僅供參考,不構成投資建議,也不代表任何形式的推薦或者誘導行為。MyFxtops邁投非外匯經紀商,不接觸妳的任何資金。 MYFXTOPS不保證客戶盈利,不承擔任何責任。從事外彙和差價合約等金融產品的槓桿交易具有高風險,損失有可能超過本金,請量力而行,入市前需充分了解潛在的風險。過去的交易成績並不代表以後的交易成績。依據各地區法律法規,MyFxtops邁投不向中國大陸、美國、加拿大、朝鮮居民提供服務。

邁投公眾號

聯繫我們

客服QQ:981617007
Email: service@myfxtop.com

MyFxtops 邁投