欢迎光临散文网 会员登陆 & 注册

量化交易软件:直方图形式的统计分布, 无需指标缓冲区和数组

2023-07-24 16:13 作者:bili_45793681098  | 我要投稿

概论

直方图允许研究人员根据其浸透到某个 (预判) 间隔的频率来直观评估统计数据组的分布。

直方图及其在统计数据分析中的使用是一个研究充分的主题, 已发表了多篇文章 [1, 2, 3, 4, 5, 6, 7], 且在代码库中有大量例程 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]。不过, 采用的算法均基于使用指标缓冲区或数组。在本文中, 赫兹量化考虑无需复杂的计算、排序、抽样等即能建立不同市场特征统计分布的可能性。为了实现这一点, 我们将使用 "图形" 内存 — 图形对象属性的部分存储。由于在绘制自定义直方图时我们需要 图形对象, 我们可以使用它们的 "隐藏" 能力和丰富的函数来满足需求。

文章的目标是为标准统计问题提供简单的解决方案。本文主要关注统计分布的可视化及其基本特征。赫兹量化不打算讨论直方图的释义及其实际优势。

编辑切换为居中

直方图绘制基础

直方图是频率的柱线图。其中一根数轴代表变量值, 另一根数轴代表这些值出现的频率。每根柱线的高度代表那些间隔等于列宽的数值的频率 (数量)。这些示意图通常水平显示, 即, 变量值位于水平轴上, 而频率 — 坐落于垂直轴。使用直方图来表示研究的数据, 使得统计数据更直观, 并且更容易理解和分析。

在本文中, 赫兹量化将关注变化序列的垂直直方图: 分析参数的价格值将按照升序位于垂直轴上, 而频率位于水平轴上 (图例. 1)。传入的价格数据在当前柱线上分布并分组, 并且可以从左侧、右侧或两侧同时相对于其数轴显示。


编辑切换为居中

图例. 1. 买卖价格分布的垂直直方图

让赫兹量化来研究一个具体的任务:

  • 绘制买卖价格分布直方图;

  • 将采购价数据定位在当前柱线的右侧, 而供给价 — 位于左侧;

  • 当新的即时报价抵达, 计算每笔入价值的频率, 即, 直方图间隔等于当前品种的最小点数大小。

现在, 我们令条件更复杂: 无指标缓冲区, 数组或结构。

如何解决这个问题?

首先, 我们要确定在哪里保存每个直方图列的累积频率。即便在图例. 1 上, 赫兹量化可以看到, 可能存在不确定数量的直方图条。首先进入脑海的是使用动态数组, 因为在价格图表的选定时间间隔上, 可能的价格范围 (柱线数量) 不可预知。但数组不允许这种问题条件。

其次, 赫兹量化应该解决搜索和排序任务: 何处以及如何搜索重新计算和重绘直方图的数据。

结果是 MQL5 语言开发人员已经创建了必要的 (和相当强大的) 功能。它基于使用图形对象功能组的 "隐藏" (非显性) 特征。每个对象都有自己的 属性 — 此变量与对象一起创建, 并用于存储各类多个参数。一些属性在使用时可以完全不同于它们的设计用途, 同时保持功能完整。赫兹量化将这些属性称之为 "图形" 内存。换言之, 如果您需要保存一个变量并接收其数值, 创建一个图形对象并将变量值赋给一个特定的属性。

所以, 图形对象属性 实际上可作为更有效的终端全局变量。全局变量自其最后访问以来可在客户终端中存在四个星期, 且随后被自动删除。图形内存在图形对象被移除之前会一直存在, 从而为我们提供了大量的机会。

赫兹量化可以在图形内存中使用哪些图形对象属性

当创建图形对象时, 我们应为它分配一个独有的名称。该名称是可以由子串组成的文本字符串,而子字符串可以包含格式化的基本数据类型:整数,布尔值,浮点数,颜色,日期和时间。因此, OBJPROP_NAME 可保存主要用来读取数据的变量。

另外其它可用的属性是 OBJPROP_TEXT 对象描述。这是一个文本字符串, 与前一个属性相比具有更大可能性的。在属性字段中可以读取和写入变量。

任何图形对象均有其自己的坐标: 价格 OBJPROP_PRICE 和时间 OBJPROP_TIME。它们也可以用在图形内存中。

让我们回到我们的目标。频率将存储在 OBJPROP_TEXT 属性, 而供给价和采购价 — 在 OBJPROP_NAME 中。创建对象和收集频率的函数代码如下:

void DrawHistogram(bool draw,     // 向左侧或右侧绘制直方图                   string h_name, // 对象名称的独有前缀                   double price,  // 价格 (分析参数)                   datetime time, // 将直方图绑定到当前柱线                   int span,      // 已分析参数的数位容量                   int swin=0)    // 直方图窗口  {   double y=NormalizeDouble(price,span);   string pfx=DoubleToString(y,span); // 如果 draw=true, 向右侧绘制直方图   if(draw)     {      string name="+ "+h_name+pfx;                   // 对象名称: 前缀+价格      ObjectCreate(0,name,OBJ_TREND,swin,time,y);    // 创建对象      ObjectSetInteger(0,name,OBJPROP_COLOR,color_R_active); // 设置对象颜色      ObjSet;                                                // 宏代码缩写      if(StringFind(ObjectGetString(0,name,OBJPROP_TEXT),"*",0)<0)        {// 如果结果价格首次进入样本         ObjectSetString(0,name,OBJPROP_TEXT,"*1");          // 价格频率为 1         ObjectSetInteger(0,name,OBJPROP_TIME,1,time+hsize); // 定义时间坐标        }      else        {// 如果结果价格并非首次进入样本         string str=ObjectGetString(0,name,OBJPROP_TEXT);    // 获取属性值         string strint=StringSubstr(str,1);                  // 获得子字符串         long n=StringToInteger(strint);                     // 获取频率用于进一步计算         n++;                                                // 数值递增 1         ObjectSetString(0,name,OBJPROP_TEXT,"*"+(string)n); // 新数值写入到属性         ObjectSetInteger(0,name,OBJPROP_TIME,1,time+hsize*n);//定义时间坐标        }     } // 如果 draw=false, 向左侧写入直方图   if(!draw)     {      string name="- "+h_name+pfx;      ObjectCreate(0,name,OBJ_TREND,swin,time,y);      ObjectSetInteger(0,name,OBJPROP_COLOR,color_L_active);      ObjSet;      if(StringFind(ObjectGetString(0,name,OBJPROP_TEXT),"*",0)<0)        {         ObjectSetString(0,name,OBJPROP_TEXT,"*1");         ObjectSetInteger(0,name,OBJPROP_TIME,1,time-hsize);        }      else        {         string str=ObjectGetString(0,name,OBJPROP_TEXT);         string strint=StringSubstr(str,1);         long n=StringToInteger(strint);         n++;         ObjectSetString(0,name,OBJPROP_TEXT,"*"+(string)n);         ObjectSetInteger(0,name,OBJPROP_TIME,1,time-hsize*n);        }     }   ChartRedraw();  }

函数由两个类似的部分组成, 分别作用于供给价和采购价。因此, 注释仅存在于第一个模块中。

您也许会问, 如果在 OBJPROP_PRICE 属性可用, 则对象名中的价格翻倍说明什么?

赫兹量化来多说一点。当新价格抵达时, 我们应为其定义适当的列, 并相应地增加频率值。 如果我们使用来自其本地属性的价格坐标, 我们务必通过所有图形对象传递, 请求属性值并将其与接收的价格进行比较。只有在这之后, 我们才能将新值写入适当的列。然而,比较实际的 double-类型数字是相当一个扯后腿的任务, 大大降低了算法的效率。。更优雅的解决方案是在新的价格值到达后, 将新频率直接写入必要的目的地。我们如何实现呢?当我们创建一个名称类似于已存在对象的新图形对象时, 新对象不会被创建, 且属性字段不会设置为零。换言之, 我们创建对象时忽略了它们将被加倍的事实。不需要额外的检查, 因为不需要创建副本和克隆。终端和图形对象功能将负责于此。赫兹量化只需要定义价格是否是首次进入样本。为此,请在 DrawHistogram() 函数的 OBJPROP_TEXT 对象属性中使用星号 (*) 前缀。没有星号表示首次进入样本的价格。实际上, 当我们创建一个新对象时, 该字段为空。具有指定前缀的频率值将在后续调用期间存储在那里。

接下来, 让我们向右侧平移直方图。当出现新的柱线时, 图表向左移动, 但是赫兹量化需要在当前柱线上显示直方图。换言之, 它应该在相反的方向上移动。以下是我们如何实现:

//--- 将直方图平移到新的柱线   if(time[0]>prevTimeBar) // 定义新柱线抵达     {      prevTimeBar=time[0];      // 通过所有图形对象传递      for(int obj=ObjectsTotal(0,-1,-1)-1;obj>=0;obj--)        {         string obj_name=ObjectName(0,obj,-1,-1);               // 获取已发现对象的名称         if(obj_name[0]==R)                                     // 搜索直方图元素前缀           {                                                    // 如果已发现直方图元素            ObjectSetInteger(0,obj_name,OBJPROP_TIME,           // 设置新坐标值                             0,time[0]);                        // 对于锚点 "0"            string str=ObjectGetString(0,obj_name,OBJPROP_TEXT);// 从对象属性中读取变量            string strint=StringSubstr(str,1);                  // 从接收的变量中分离出一个子字符串            long n=StringToInteger(strint);                     // 将字符串转换为长整型变量            ObjectSetInteger(0,obj_name,OBJPROP_TIME,           // 计算新坐标值                             1,time[0]+hsize*n);                // 对于锚点 "1"            ObjectSetInteger(0,obj_name,OBJPROP_COLOR,                             color_R_passive);                  // 改变已移动直方图元素的颜色           }         if(obj_name[0]==L)           {            ObjectSetInteger(0,obj_name,OBJPROP_TIME,0,time[0]);            string str=ObjectGetString(0,obj_name,OBJPROP_TEXT);            string strint=StringSubstr(str,1);            long n=StringToInteger(strint);            ObjectSetInteger(0,obj_name,OBJPROP_TIME,1,time[0]-hsize*n);            ObjectSetInteger(0,obj_name,OBJPROP_COLOR,color_L_passive);           }        }      ChartRedraw();     }


量化交易软件:直方图形式的统计分布, 无需指标缓冲区和数组的评论 (共 条)

分享到微博请遵守国家法律