外汇EA编写教程:MTA Trader 5图表上的水平草图

总结

水平草图在终端图表中并不常见,但在许多任务中仍然使用,例如在为各种版本创建市场深度时,开发显示特定周期的数量或价格分布的指标。在定制(标准)指标值的分布方面,可能会有更多的异国任务。但是,它们都有一个共同的特征——应该创建、放置、缩放、移动和删除示意图。我们要强调以下几点:

  1. 可能有几个草图(在大多数情况下是这样)。
  2. 我们感兴趣的大多数草图都是由柱状线条组成的。
  3. 绘制柱水平排列。

让我们以一个著名的例子来看看这些草图的外观:

这是另一个例子。由图形原语绘制的相同示意图:

在这种情况下,它是报价交易量分布的每日指标。这个例子清楚地揭示了开发人员必须解决的任务:

  • 创建许多图形对象,为它们指定唯一的名称,并将它们放在图表上。
  • 根据需要缩放和移动对象。
  • 指标工作完成后,将从图表中删除。

再次,我们应该注意到,有几个图表将使我们能够讨论未来的示意阵列。

下面是最后一个例子:

在这里,我们可以看到每种报价的更复杂的版本。公司章程:

  • “按报价量销售报价”
  • “按购买报价交易量”
  • “按报价量列出的总报价”

你可能会问,“有没有更简单的方法来显示数据?我们需要管理这么多的图形原语吗?”的确,可以找到更简单的方法,并对其效率进行分析。然而,解决本文开头提到的整个任务集的最简单方法是使用示例中所示的水平草图。

设置任务

我们主要分两部分概述我们的计划:

  1. 由于所有图形原语的坐标都受时间和价格的约束,因此很明显,我们应该收到一组绑定在图表上的定位坐标。
  2. 使用在第一阶段获得的阵列,有必要显示和管理原理图。

基于我们的例子,我们定义了绑定图形对象的主要方法。第一个屏幕截图大致显示了最常见的水平草图排列。它们是在循环开始时定义的。在这里,这是一天的开始。

当然,这不会耗尽时间坐标绑定选项的列表。另一个选项是绑定终端窗口的左侧或右侧。例如,当草图被长时间覆盖并且开始位于可视窗口之外时,可以使用它。在这种情况下,可以使用绑定窗口的左侧。

另一种选择是将草图绑定到当前循环,同时避免在图表的工作部分有太多的对象。在这种情况下,可以使用终端的右侧。在任何情况下,构成草图的图形基本体的时间坐标之一都保持不变。计算另一个坐标(计算示意图的“柱水平长度”坐标)。

对于图形原语的价格约束,情况要简单得多。价格段按固定步骤划分为区间。例如,可以假设价格段等于100%,速度等于10%。在这种情况下,我们可以得到一个“水平柱状线”数量不变的示意图,这在某些情况下会导致相当大的舍入。因此,在本文中,我们将采用一种更为有效的方法,这将在后面的研究中加以研究。
的结论是,在这种情况下,可能不需要价格绑定数组。我们可以使用以下简单公式计算第一个价格约束:

第 i 个价格绑定 = 示意图喀什价格间隔 + i * 步幅间距。

对于所提供的按报价量报价的示例,可以构建草图的价格范围,并通过检查周期中最低价格和最高价格之间的间隔来表示。

还应详细说明原理图显示的问题。因此,我们根据以下原则获得了草图的绑定数组:“一个草图-一组用于绑定的数组”。另一个明显的观察是图表中包含的示意图通常基于相同的颜色和类似的“样式”图形原语(例如,第二个和第一个屏幕截图中的示意图)。在第三个屏幕截图中,所有草图根据颜色和方向而变化。一个素描专栏“从右向左”,另一个从左到右。将“相同类型”的草图(例如前两个截图)组合成一个数组并指派一个管理者来控制它是合乎逻辑的。我们采用以下原则进一步规范任务:

  • 即使集合只包含一个草图,每个“同类型”草图组都应该有自己的管理者。因此,对于前两个屏幕截图,我们创建一个由一个管理器管理的至少三个草图的数组,对于第三个屏幕截图,我们必须创建三个数组(每个数组有一个草图)和三个管理器(每个数组对应一个)。

到目前为止,我们已经概述了主要的发展点。但也有一些基本的特殊性。您可能还记得,水平草图显示了各种分布,如报价量、价格等。因此,获取用于绑定图形的基元数组的方法和原理可能非常不同,为这部分任务创建库文件是不合理的。

特别是,在制定每日报价量的培训指标时,将采用更有效的方法。本文提供了另一种方法。换句话说,任务的第一部分将根据不同的情况以不同的方式解决。相反,任务的第二部分(在管理员的帮助下开发一组草图和管理数组,或者开发几个“管理器草图数组”关联)在所有情况下几乎都是相同的。这允许创建一个库文件,该文件可以包含在涉及水平草图的所有项目中。

最后一件我们应该指定的是草图的组成部分。草图将由水平线或矩形组成。这是两个最自然的选择。

现在让我们直奔代码。

常数和输入

显然,上面提到的许多示意图参数是使用枚举类型存储的。

示意位置:

enum HD_POSITION
{
        HD_LEFT  = -1,
        HD_RIGHT =  1,
        HD_CNDLE =  2 
};

有三个示意图放置选项-绑定到终端的左侧(hd_-left),绑定到右侧(hd_-right),绑定到蜡烛(hd_-cndle)。在本文开头的三个截图中,使用hd_cndle放置示意图。在前两种情况下,在某一时期的开始(白昼线的开始)进行蜡烛绑定,而在第三种情况下,在一天的开始进行单个蜡烛绑定。

草图外观(图形基本体):

enum HD_STYLE 
{
        HD_LINE      = OBJ_HLINE,        
        HD_RECTANGLE = OBJ_RECTANGLE,    
};

有两个外观选项-水平直线段(hd_line)和矩形(hd_rectangle)。在本文开头的第一和第三个屏幕截图中,草图由HD_线原语组成,而在第二个屏幕截图中,草图由HD_矩形组成。

示意图中“水平条”的方向:

enum HD_DIRECT 
{
   HD_LEFTRIGHT = -1,
   HD_RIGHTLEFT =  1 
};

在第三个屏幕截图中,包含红线的示意图显示为hd_rightleft,而其他两个显示为hd_leftright。

最后一个枚举类型与示意图级别的数量相关。我提到计算水平数量的最佳方法不是简单地将价格范围划分为给定的水平数量。请注意,此枚举类型与安装任务的第一部分相关,因此它不会包含在最终的库文件中。

使用的方法非常简单,将价格系列四舍五入到最接近的10或100。相应地,价格序列的步数将等于10或100。这样,价格系列的数量就会发生变化。对于那些愿意获得最大计算货币(以及增加的资源消耗)的人,保留不带舍入的hd_min方法:

enum HD_ZOOM {
   HD_MIN    = 0,  //1
   HD_MIDDLE = 1,  //10
   HD_BIG    = 2   //100
}; 

默认情况下应用hd_middle方法.

我们来看下面的例子。我们将使用显示报价交易量分布的培训指标作为发展目标。本文开头的前两个屏幕截图中提供的类似指标的工作可以用作示例。

我们转到输入模块:

input HD_STYLE        hdStyle      = HD_LINE;
input int             hdHorSize    = 20;
input color           hdColor      = clrDeepSkyBlue;
input int             hdWidth      = 2;
input ENUM_TIMEFRAMES TargetPeriod = PERIOD_D1;
input ENUM_TIMEFRAMES SourcePeriod = PERIOD_M1;
input HD_ZOOM         hdStep       = HD_MIDDLE;   
input int             MaxHDcount   = 5;
input int             iTimer       = 1;   

为什么没有参数负责定位?答案是显而易见的。只有一种定位方法适用于所需的指示器-HD-CNDLE。因此,我们可能不需要具体说明。

hd_型参数的功能非常明显,无需进一步解释。

  • hdhorsize参数起着重要作用。它定义了蜡烛草图中水平条的最大大小。在这种情况下,最长的“水平条”不应超过二十根蜡烛。显然,参数值越大,草图就越精确。但是,如果参数值太大,草图就会开始重叠。
  • HDLoad和HDLoad参数处理草图的外观(对应的颜色和线宽)。
  • 目标周期包含用于分析的时间帧。在这种情况下,指标显示一天内报价交易量的分布。
  • SURCEVILATH参数包含索引构造分布时提取源数据的时间帧。在这种情况下,默认情况下使用M1时间帧。请仔细使用这个参数。如果选择每月时间框架作为分析时间框架,则计算可能需要很长时间。
  • hdstep参数是舍入价格系列。我已经在上面描述过了。
  • maxhdcount参数包含图表上示意图的最大数目。请记住,每个草图都包含多个图形基本体。过多的草图可能会减慢终端操作。
  • ITIMER参数包含计时器的触发频率。触发后,将检查新蜡烛的创建,并执行必要的操作。PeriodSeconds(SourcePeriod)调用结果可能已放在此处。但默认值是一秒钟,这使我们能够更准确地确定新蜡烛出现的确切时刻。

初始化

在这个阶段,我们需要为草图管理器创建一个对象。因为所有的草图都是同一类型的,所以我们只需要一个经理。由于管理器本身的类尚未写入,请记住它是在OnInit()处理程序中创建的。这里还创建了两个草图(但未绘制):

  1. 显示当前周期中报价交易量分布的示意图。这个草图将定期重新绘制。
  2. 根据历史记录,给出了报价交易量的示意图。这些分布不会重新绘制,因此草图在显示时会“忘记”它们,并将控制权交给终端。

所提出的方法的有效性是没有对示意图的图形原语进行处理,因此所提出的方法的外观不会改变。

接下来,变量被初始化,使得价格序列可以通过步骤计算。有两个这样的变量。它们是从数字()和点()导出的:

   hdDigit = Digits() - (int)hdStep; 
   switch (hdStep)
    {
      case HD_MIN:
         hdPoint =       Point();
         break;
      case HD_MIDDLE:
         hdPoint = 10 *  Point();
         break;     
      case HD_BIG:
         hdPoint = 100 * Point();
         break;      
      default:
         return (INIT_FAILED);
    }

一些小动作和启动计时器也发生在这个处理程序中。

基本计算

下一个任务分为两个阶段:

  1. 计算所需数据,并绘制除当前数据外所需数据量的示意图。这些草图不会被更改,它们只会显示一次。
  2. 计算当前周期的必要数据,包括当前时间,并绘制示意图。这一点应该定期重新计算。这可以在ontimer()处理程序中完成。

我们在oncalculate()处理程序中以伪代码的形式执行一些工作:

int OnCalculate(...)
  {
   if (prev_calculated == 0 || rates_total > prev_calculated + 1) {
   }else {
      if (!bCreateHis) 
       {
         int br = 1;
         while (br < MaxHDcount) {
           {
            if(Calculate for bar "br") 
                 {
                  sdata.bRemovePrev = false;
                  Print("Send data to the new Diagramm");
                 }

           }
         ChartRedraw();
         bCreateHis = true;
      }
   }  
   return(rates_total);
  }  

进行必要的计算,并在此绘制除当前计算之外所需数字的示意图。为了实现这一目标,在TargetPeriod时间帧周期中计算每一列,从第一列开始,到MaxhCount结束。如果计算成功,将向管理器发出命令,以绘制管理器在同一循环中接收到的新数据。循环结束时,重新绘制鱼体,并设置一个标志以指示不再需要这部分工作。图表本身现在由终端控制。

在ontimer()处理程序中创建并绘制包含当前循环的示意图。因为这个问题显而易见,我不打算在这里显示伪代码:

  1. 在SourcePeriod时间范围内等待新蜡烛。
  2. 进行必要的计算。
  3. 将数据发送到当前循环的示意图以创建和绘制新的基本体。
我们研究函数,主要计算某个 TargetPeriod 时间帧内的柱线,则稍后执行。

其他处理程序和指针函数不是很有趣,可以在附带的代码中找到。现在是时候描述负责创建、绘制和管理草图的类了。

素描管理课

让我们从处理水平草图的管理器开始。此类本身不包含图形基元,但管理包含此类基元的其他类的数组。因为所有草图都是同一个管理器中的同一类型(如上所述),所以这些草图的许多属性都是相同的。因此,在每个示意图中维护相同的一组属性是毫无意义的。相反,在管理者中放置一组属性是值得的。我们把经理类命名为“CHIDES”并开始编码:

  1. &chdiags类的封装字段包含一组属性,这些属性对于受此管理器控制的所有示意图都是相同的:
private:    
                HD_POSITION m_position;  
                HD_STYLE    m_style;     
                HD_DIRECT   m_dir;       
                int         m_iHorSize;     
                color       m_cColor;    
                int         m_iWidth;     
                int         m_id;
                int         m_imCount;       
                long        m_chart;    
                datetime    m_dtVis; 
   static const string      m_BaseName;  
                CHDiagDraw* m_pHdArray[];

数据集描述:

  • M_位置,M_样式,M_目录-这三个参数描述了原理图的绑定和外观。我们已经描述过了。
  • M_ihorsize-示意图的最大可能水平尺寸。我们也描述过它。
  • M颜色和M宽度-草图颜色和线条宽度。
上述字段是管理器所处理的所有示意图的统一属性。
  • m_id-唯一的管理器id。由于可能有多个管理器,每个管理器都应该有一个唯一的id。需要形成一个唯一的对象名。
  • M_chart——显示原理图的图表ID。默认情况下,此字段的值为零(当前图表)。
  • M?图表上草图的最大数量。最终,图表上的示意图数量由此字段和以下字段确定。
  • 不要在这个时间戳的左边创建示意图。
  • m_base name——定义名称“base”的一个非常重要的参数。草图和草图本身的所有元素都应具有唯一的名称,以便成功创建。这些名称以“基本”名称的后面给出。
使用 GetXXXX() 形式的函数可以获得上述所有字段
  • m eu phdarray[]—包含指向独立原理图对象的指针数组。此字段不是属性,没有对应的getXXXX()函数。

此属性没有对应的SETXXX()函数。所有这些(除了MyBaseNeNm)都设置在类构造函数中。m_dtvis字段是另一个例外。它由bool类型参数在构造函数中设置,具有以下含义:

  • 示意图仅显示在终端窗口中可见的蜡烛上。这样做是为了通过不在终端边界左侧显示示意图来减少终端负载。默认值为“真”。

一旦创建了管理器,我们就可以创建一个对象草图。这是通过chdiags类方法完成的:

int CHDiags::AddHDiag(datetime dtCreatedIn)

此方法返回在管理器的m_phdarray数组中创建的chdiagdraw类对象的索引,如果发生错误,则返回-1。原理图开始计时作为参数传递给dtcreatedIn方法。例如,每天打开蜡烛的时间传递给分析的指标。如果不使用时间戳(蜡烛绑定到终端窗口边框),则应在此处传递timecurrent()。如果草图位于m_tvis字段时间戳的左侧,则不会创建对象。以下代码显示了此方法的工作原理:

int CHDiags::AddHDiag(datetime dtCreatedIn) {
   if(dtCreatedIn < m_dtVis ) return (-1);
   int iSize = ArraySize(m_pHdArray);
   if (iSize >= m_imCount) return (-1);
   if (ArrayResize(m_pHdArray,iSize+1) == -1) return (-1);
   m_pHdArray[iSize] = new CHDiagDraw(GetPointer(this) );
   if (m_pHdArray[iSize] == NULL) {
      return (-1);
   }
   return (iSize);
}//AddHDiag()

如您所见,此方法执行一些检查,增加数组的大小以保存原理图,创建所需的对象,并将指针传递给管理器本身以供以后访问属性。

管理器还提供了其他方式来允许您与草图交互,而不是直接交互,而是仅通过草图管理器交互:

   bool        RemoveDiag(const string& dname);
   void        RemoveContext(int index, bool bRemovePrev);
   int         SetData(const HDDATA& hddata, int index); 
  1. 从第一个方法的名称可以清楚地看到,它将原理图名称作为参数,从管理器和图中完全删除原理图。目前,这是一个保留选项。
  2. 第二个只删除原理图中包含的图形原语。原理图从图中删除,但仍存在于管理器中,其中“空”。进一步解释了bremoveprev标志值。
  3. 第三种方法使用输入数据传输结构来创建图形原语并绘制原理图。管理器m_phdarray数组中的原理图索引用作当前和以前方法中的参数。

最后一个值得一提的方法是chdiags类方法:

void        Align();

如果原理图绑定到终端窗口的左侧或右侧,则调用此方法。在这种情况下,在ChartEvent_Chart_Change事件的OnChartEvent处理程序中调用该方法,并将原理图返回到其以前的位置。

chdiags草图管理器类的其他方法是辅助的,可以在其他文件中找到。

用于绘制和管理草图图形原语的

我们称这个类为“chdiagdraw”,并从cobject派生它。在类构造函数中,我们得到指向管理器的指针(将其保存在m_prop字段中)。这里还定义了一个唯一的原理图名称。

接下来,我们应该实现类型()方法:

int CHDiagDraw::Type() const
  {
   switch (m_pProp.GetHDStyle() ) {
      case HD_RECTANGLE:
         return (OBJ_RECTANGLE);
      case HD_LINE:
         return (OBJ_TREND);
   }
   return (0);
  }

显示的示意图类型与应用的图形原语类型匹配是合理的。

在chdiagdraw类中执行主计算的方法由管理器的setdata方法调用:

int       SetData(const HDDATA& hddata);

此方法的目标是定义示意图的大小,并在图中的某个点创建必要数量的基本体。为此,在调用点传递到结构实例的链接:

struct HDDATA
 {
   double   pcur[];
   double   prmax; 
   double   prmin;   
   int      prsize;
   double   vcur[];
   datetime dtLastTime;
   bool     bRemovePrev;
 };

我们更详细地描述了结构字段:

  • PCUR[]——价格组示意图。对于创建的图形原语,这是一个价格绑定数组。
  • prmax-示意图可以达到的最大级别。在这种情况下,这是特定级别上报价交易的最大数量。
  • prmin-保留参数。
  • prsize-示意图的数量级。换句话说,这是原理图将包含的元素数。
  • VCUR[]—一个数字数组,用于定义原理图中条形的“水平尺寸”。在这种情况下,数组包含PCUR[]数组中相应级别的引用事务量。PCUR和VCUR数组的大小应匹配并等于PRSIZE。
  • dtlasttime-示意图位置。对于图形原语,这是一个时间绑定。此字段优先于管理器的addhdiag方法的参数。
  • bremove prev-如果为“真”,则彻底重新绘制草图,同时删除以前的图形原语。如果设置为“假”,草图将停止管理以前的图形基本体,并在不删除它们的情况下绘制新草图,就像忘记它们一样。

由于其重要性,下面提供了setdata方法代码:

int CHDiagDraw::SetData(const HDDATA &hddata) 
  {
   RemoveContext(hddata.bRemovePrev);
   if(hddata.prmax == 0.0 || hddata.prsize == 0) return (0);
   double dZoom=NormalizeDouble(hddata.prmax/m_pProp.GetHDHorSize(),Digits());
   if(dZoom==0.0) dZoom=1;
   ArrayResize(m_hItem,hddata.prsize);
   m_hItemCount=hddata.prsize;
   int iTo,t;
   datetime dtTo;

   string n;
   double dl=hddata.pcur[0],dh=0;
   

   GetBorders(hddata);
   for(int i=0; i<hddata.prsize; i++) 
     {
      if (hddata.vcur[i] == 0) continue;
      t=(int)MathCeil(hddata.vcur[i]/dZoom);
      switch(m_pProp.GetHDPosition()) 
        {
         case HD_LEFT:
         case HD_RIGHT:
            iTo=m_iFrom+m_pProp.GetHDPosition()*t;
            dtTo=m_pProp.GetBarTime(iTo);
            break;
         case HD_CNDLE:
            iTo   = m_iFrom + m_pProp.GetHDDirect() * t;
            dtTo  = m_pProp.GetBarTime(iTo);
            break;
         default:
            return (-1);
        }//switch (m_pProp.m_position)
      n=CHDiags::GetUnicObjNameByPart(m_pProp.GetChartID(),m_hname,m_iNameBase);
      m_iNameBase++;
      bool b=false;
      switch(m_pProp.GetHDStyle()) 
        {
         case HD_LINE:
            b=CHDiags::ObjectCreateRay(m_pProp.GetChartID(),n,dtTo,hddata.pcur[i],m_dtFrom,hddata.pcur[i]);
            break;
         case HD_RECTANGLE:
            if(dl!=hddata.pcur[i]) dl=dh;
            dh=(i == hddata.prsize-1) ? hddata.pcur[i] :(hddata.pcur[i]+hddata.pcur[i+1])/2;
            b = ObjectCreate(m_pProp.GetChartID(),n,OBJ_RECTANGLE,0,dtTo,dl,m_dtFrom,dh);
            break;
        }//switch(m_pProp.m_style)
      if(!b) 
        {
         Print("ERROR while creating graphic item: ",n);
         return (-1);
           } else {
         m_hItem[i]=n;
         ObjectSetInteger(m_pProp.GetChartID(), n, OBJPROP_COLOR, m_pProp.GetHDColor() );
         ObjectSetInteger(m_pProp.GetChartID(), n, OBJPROP_WIDTH, m_pProp.GeHDWidth() );
         ObjectSetInteger(m_pProp.GetChartID(), n, OBJPROP_SELECTABLE, false);
         ObjectSetInteger(m_pProp.GetChartID(), n, OBJPROP_BACK, true);
        }//if (!ObjectCreateRay(n, dtTo, hddata.pcur[i], m_dtFrom, hddata.pcur[i]) )    
     }// for (int i = 0; i < l; i++)      
   return (hddata.prsize);
  }//int CHDiagDraw::SetData(const HDDATA& hddata)

这个方法的第一件事是清理和计算比例。它还具有示意图“水平条”的最大长度和示意图的最大水平尺寸(以烛光为单位)。我们可以得到这个比率。很容易注意到舍入是强制执行的,因为“以蜡烛为单位”的图形大小是一个整数。

接下来,准备存储基元名称的数组。计算其他草图绑定参数-蜡烛索引和getborders方法中的临时绑定。原理图的第二次绑定在随后的循环中定义。因此,用于创建图形基元的所有绑定都已就位。我们得到原语的唯一名称,并按照接收参数的顺序创建它们。基元的名称存储在数组中,并且它们的属性已被更正。草图已创建。此方法返回原理图系列的数目。

也许这个方法太长了。可以将用于创建和呈现基元的代码移动到单独的保护方法中。然而,方法的两个部分似乎是有机兼容的,第二部分显然是第一部分的延续。考虑到不愿意创建使用大量参数的新方法,所开发的setdata方法以其当前形式存在。

chdiagdraw类的其他函数是用于与管理器集成的辅助函数。

用户不直接调用chdiagdraw类的任何方法。相反,它们只能通过水平草图管理器执行。

上面提到的水平草图管理器类、草图绘图类、结构和枚举的整个代码可以在所附的hdiagse中找到。MQH文件。

现在我们可以返回EA代码并详细检查其内容,而不使用伪代码。

返回指示器

在全局上下文中阐明两个对象和变量:

CHDiags    *pHd;
int         iCurr, iCurr0;
HDDATA      sdata;

这些是指向原理图管理器、原理图索引以及向原理图结构传输数据的指针。

在指示器输入的基础上立即创建OnInit()中的管理器和示意图:

   pHd         = new CHDiags(HD_CNDLE, hdStyle, HD_LEFTRIGHT, hdHorSize, hdColor, hdWidth, 0, MaxHDcount);
   if(pHd      == NULL) return (INIT_FAILED);
   iCurr       = pHd.AddHDiag(TimeCurrent() );
   if(iCurr  == -1) return (INIT_FAILED); 
   iCurr0       = pHd.AddHDiag(TimeCurrent() );
   if(iCurr0  == -1) return (INIT_FAILED);    

下面是OnPalm处理器的一部分,我们必须使用最近的伪代码:

        {
         int br=1;
         while(br<MaxHDcount) 
           {
            if(PrepareForBar(br++,sdata)) 
              {
               sdata.bRemovePrev = false;
               if(iCurr!=-1) 
                 {
                  Print(br-1," diag level: ",pHd.SetData(sdata,iCurr));
                 }
              }
           }
         ChartRedraw();
         bCreateHis=true;
        }

现在我们只需要考虑填充HDDATA类型结构的函数,它包含目标周期时间帧内选定的条带的数据:

bool PrepareForBar(int bar, HDDATA& hdta) {

   hdta.prmax  = hdta.prmin  = hdta.prsize  = 0;
   int iSCount;
   datetime dtStart, dtEnd;
   dtEnd = (bar == 0)? TimeCurrent() : iTime(Symbol(), TargetPeriod, bar - 1);
   hdta.dtLastTime = dtStart = iTime(Symbol(), TargetPeriod, bar);
   
   hdta.prmax = iHigh(Symbol(), TargetPeriod, bar);
   if(hdta.prmax == 0) return (false);
   hdta.prmax      = (int)MathCeil(NormalizeDouble(hdta.prmax, hdDigit) / hdPoint );
   
   hdta.prmin = iLow(Symbol(), TargetPeriod, bar);
   if(hdta.prmin == 0) return (false);
   hdta.prmin      = (int)MathCeil(NormalizeDouble(hdta.prmin, hdDigit) / hdPoint );

   iSCount = CopyRates(Symbol(), SourcePeriod, dtStart, dtEnd, source);
   if (iSCount < 1) return (false);
   
   hdta.prsize = (int)hdta.prmax - (int)hdta.prmin + 10;
   
   ArrayResize(hdta.pcur,  hdta.prsize);
   ArrayResize(hdta.vcur,  hdta.prsize);
   ArrayInitialize(hdta.pcur, 0);
   ArrayInitialize(hdta.vcur, 0);
   
   double avTick;
   int i, delta;
   hdta.prmax = 0;
   
   for (i = 0; i < hdta.prsize; i++) hdta.pcur[i] = (hdta.prmin + i) * hdPoint;
   int rs = 0;
   for (i = 1; i < iSCount; i++) {
      if (source[i].tick_volume == 0.0) continue;
      if (!MqlRatesRound(source[i], (int)hdta.prmin) ) continue;
      delta = (int)(source[i].high - source[i].low);
      if (delta == 0) delta = 1;
      avTick = (double)(source[i].tick_volume / delta);
      int j;
      for (j = (int)source[i].low; j <= (int)(source[i].low) + delta; j++) {
         if (j >= hdta.prsize) {
            Print("Internal ERROR. Wait for next source period or switch timeframe");
            return false;
         }
         hdta.vcur[j] += avTick;
         if (hdta.vcur[j] > hdta.prmax) hdta.prmax = (int)hdta.vcur[j];
      }//for (int j = (int)source[i].low; j <= (int)(source[i].low) + delta; j++)   
      if (j > rs) rs = j; //real size
   }//for (int i = 1; i < iSCount; i++)
   hdta.prsize = rs + 1;
   return (true);
}  

在开始时,该方法找出它应该执行计算的时间段。在此基础上,计算了工作价格范围的边界,并进一步划分为图纸系列。

mqlrates从最新指定的时间段中读取源数据。对于获得的每个mqlrates结构,Tick_体积被认为在从低到高的范围内均匀分布。由于已知整个未来草图的价格范围边界,因此报价交易的数量分布位于草图中。因此,形成了在所需时间内的逐笔报价交易量分布数字组。

在计算结束时,定义了每个报价单交易量分布数组的实际大小,并相应地定义了未来草图中的图形基元数。

因此,草图的数据结构字段将被填充,并准备好传递给setdata(…)方法。

通常,所描述的类如下所示:

  1. 连接hdiags e.mqh。
  2. 为每一组“同类型”草图创建一个管理器对象。
  3. 调用addhdiag管理器方法创建原理图。此方法返回管理器已知数组中的索引。
  4. 调用removeContext管理器方法从不相关的草图中清除数据。调用setdata管理器方法并将hddata类型结构及其数据传递给它,将新数据传递给原理图。调用方有责任正确填写此结构字段。
  5. 如果需要,可以通过调用Alin管理器方法将草图与终端窗口的左侧或右侧对齐。
  6. 所有示意图都在chdiags管理器类分析结构函数中注销.

完整的指示器代码可以在所附的volchart中找到。MQ5文件。库文件hdiagse.mqh也附加在下面。

姓名

几乎所有与水平草图相关联的对象都有名称。它们的结构如下:

  1. 管理器包含定义名称“base”的m_base name私有字段。其他的名字都是从它开始的。
  2. 创建管理器对象时,会为其分配唯一的ID。调用代码负责此参数的唯一性。m_basename字段和id构成管理器的名称。
  3. 创建草图对象时,它还将收到基于管理员名称的唯一名称。
  4. 最后,在草图对象中创建的图形原语根据草图对象的名称获得其唯一的名称。

这样,就可以轻松地对必要的对象进行排序和管理。

还有一个指示器

我们将已制定的指标修改成图表,显示每个报价的数量分布,并将其与终端窗口的右侧对齐:

要执行此操作,请稍微更改代码:

  • 初始代码应略为更改phd=new chdiags(hd right,hdstyle,hd rightleft,hdhorsize,hdcolor,hdwidth,0,maxhdcount);if(phd==null)return(init失败);icurr=addhdiag(time current();if(icurr=1)return(init_failed);
  • 从oncalculate处理程序中删除所有代码。nbsp;
  • 添加onchartevent处理程序
    void onchartevent(const int id,const long&lparam,const double&amp;p;dparam,case chartevent图表更改:b sp;phd.align();break;break;break;默认值:break;break;/switch(id)
就这些。 得到新指标且它可以工作。 其完整代码在下面附带的 VolChart1.mq5 文件中提供。

结束语

现在我们可以通过启用单个文件来创建水平草图。开发人员的任务将包括准备构建数据,这是本文的主题之外。对于保留方法,建议尽可能地改进库文件代码。可以为绘图添加其他图形基元。

别忘了,附加指标仅用于演示和培训。不要在实际交易中使用它们。尤其要记住,在用作数据源的时间范围内不应该存在任何控件。

本条中使用的程序和文件

nbsp; 姓名 描述
VQCART.MQ5 索引 报价交易量分布指数。
2 哈格斯 库文件 包含水平草图管理器和水平草图的库文件。
3 电压表1.MQ5 指示器 逐笔竞价交易量分布指数与终端窗口右侧对齐。

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

附加文件下载zip hdiags e.mqh(36.66 kb)、volchart.mq5(12.65 kb)、volchart 1.mq5(12.21 kb)

 

 


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

 

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

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

風險提示

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

邁投公眾號

聯繫我們

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

MyFxtops 邁投