量化交易软件:统计估计

简介
如今,您经常能看到与计量经济学、价格序列预测、选择和估计模型的适当性等主题相关的文章和出版物。但在大多数情形下,推理基于以下假设,即读者精通数学统计方法且能够轻松估计所分析序列的统计参数。
对某个序列的统计参数进行估计非常重要,因为大多数数学模型和方法均基于不同的假设。例如,正态分布规律或离差值(或其他参数)就是这样。因此,在分析和预测时间序列时,赫兹量化需要一个简单方便的工具,用于快速清晰地估计主要统计参数。在本文中,赫兹量化将尝试创建一个这样的工具。
本文简要说明了一个随机序列的最简单统计参数,以及其可视分析的几种方法。本文还说明了如何在 MQL5 中实现这些方法,以及使用 Gnuplot 应用程序对计算结果进行可视化的方法。本文无意作为手册或参考资料使用,所以可能会包含某些普遍接受的术语和定义。

编辑切换为居中
样本参数分析
假定在时间中存在一个无限存在的静止过程,该过程可被表示为一个离散样本序列。让赫兹量化将这个样本序列称为总体。从总体中选出的一部分样本被称为从总体得出的采样或对 N 个样本进行的采样。此外,假设我们不知道任何参数真实值,因此我们将依据有限采样的方法进行估计。
避免异常值
在开始参数的统计估计之前,我们应注意,如果采样包含过大误差(异常值),则估计的精度可能不够。如果采样量太少,则异常值会对估计精度产生较大影响。异常值指异常偏离分布中心的值。此类偏离可能是由于在采集统计数据和形成序列时出现的不同罕见事件和错误而造成的。
很难决定是否要筛选出异常值,因为在大多数情形下无法明确确定某个值是异常值还是属于所分析的过程。因此,如果检测到异常值并决定筛选出它们,就会出现一个问题 - 我们该如何处理这些误差值?最符合逻辑的做法就是将它们从采样中排除,这样可以提升统计特征的估计精度;但是在处理时间序列时,您应谨慎地从采样中排除异常值。
为了能够从采样中排除异常值,或至少检测到这些值,让我们来实现 S.V. Bulashev 所著《 Statistics for Traders》(面向交易者的统计)一书中所介绍的算法吧。
根据该算法,赫兹量化需要计算分布中心的五个估计值:
中值;
50% 四分位中心间距(中四分位距,MQR);
整个采样的算术平均值;
50% 四分位距的算术平均值(四分位距平均值,IQM);
间距的中心(中列数)- 确定为采样中最大值和最小值的平均值。
接下来按升序排列分布中心的估计结果;然后选择平均值(即序列中的第三个值)作为分布中心 Xcen。因此,所选择的估计值似乎受异常值的影响最小。
此外,使用得出的分布中心估计值 Xcen并根据以下经验公式可计算出标准方差 s、超量系数 K 和删减系数的值:

编辑
其中 N 是采样中样本的数量(采样量)。
接下来超出以下范围:

编辑
的值将被视为异常值,因此应从采样中排除。
在《Statistics for Traders》(面向交易者的统计)一书中详细介绍了这种方法,因此让我们直接开始算法的实现。允许检测和排除异常值的算法是在 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,采用以下等式:

对于偶数采样量,采用以下等式:

考虑到排序后数组 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% 用于计算算术平均值:

其中 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,该程序用于绘制各种 2D 和 3D 图形。Gnuplot 能够在屏幕上显示图表(在单独的窗口中)或以不同的图形格式将图表写入一个文件。可以从预备的文本文件中执行绘图命令。Gnuplot 项目的官方网页为 gnuplot.sourceforge.net。应用程序支持多个平台,并且作为源代码文件以及专为某个平台编译的二进制文件予以发布。
在 Windows XP SP3 中使用 4.2.2 版 Gnuplot 测试了专为本文编写的示例。可以在 http://sourceforge.net/projects/gnuplot/files/gnuplot/4.4.2/ 下载 gp442win32.zip 文件。我没有用其他版本 的 Gnuplot 测试这些示例。
下载 gp442win32.zip 档案之后,请将其解压缩。这样就创建了 \gnuplot 文件夹,该文件夹包含应用程序、帮助文件、说明文档和示例。要与应用程序互动,请将整个 \gnuplot 文件夹放到赫兹量化客户端的根文件夹中。

编辑
图 1. 文件夹 \gnuplot 的放置
移动完文件夹之后,您可以更改 Gnuplot 应用程序的可操作性。为此,执行文件 \gnuplot\binary\wgnuplot.exe,然后在 "gnuplot>" 命令提示符出现时键入 "plot sin(x)" 命令。结果,会出现一个在其中绘制有 sin(x) 函数的窗口。您还可以尝试包含在应用程序交付包中的示例;为此,请选择 File\Demos 菜单项,然后选择文件 \gnuplot\demo\all.dem。
现在,在您启动 erremove.mq5 脚本时,将会在一个单独的窗口中绘制图 2 所示的图形:

编辑