English Русский Deutsch 日本語 Português
preview
创建多交易品种、多周期指标

创建多交易品种、多周期指标

MetaTrader 5指标 | 3 四月 2024, 09:35
652 0
Artyom Trishkin
Artyom Trishkin

目录


概述

继续为 EA 交易和指标中的技术指标创建模板的主题,该主题从与创建仪表盘相关的文章中开始,然后在另外三篇文章中发展,其中我们探讨了振荡器交易量指标和比尔威廉姆斯以及趋势指标,今天我们将开始创建多交易品种、多周期指标的主题。其中包括在当前图表上运行但使用其他交易品种和(或)其他图表时间框架的数据计算的指标。我们将创建一个基础多指标类和一组基于所有标准指标类型的类。我们还将为自定义指示器创建一个类,使用该类可以将任何指标“转换”为多交易品种和多周期指标。对于所有指标类,我们将创建一个统一集合类。程序中创建的所有指标都将被放入此集合中。此外,还可以使用方法访问集合中任何创建的指标,以获得相关的指标数据。最终目标是开发一种方便的工具,用于创建多指标和处理其数据。


基本原则

为了正确理解指标操作的逻辑,让我们试着了解它是如何工作的。指标分为两部分:计算和绘图。这些部分中的每一个都对另一个一无所知。当创建指标时,终端子系统在图表上查找这样的指标是否存在。在这种情况下,它会查找具有相同名称和参数的指标。如果这样的指标已经在图表上运行,或者已经为此图表以编程方式创建,则终端将使用现有指标的句柄,而不是创建新指标。指标的绘图部分使用其句柄从计算部分接收所需的数据。可能存在多个绘图部件同时访问一个计算部件的情况。

计算部分的缓冲区将计算出的指标的数据存储在具有从现在到过去排列的数据的数组中。缓冲区数组索引0处的数据对应于当前图表数据:

数组的每个单元存储一个柱形图的数据,该柱形图对应于计算指标的交易品种/周期的时间序列柱形图。因此,为了从指标计算部分的缓冲区中获得数据并将其显示在另一个交易品种/时间框架的图表上,您需要计算计算部分缓冲区数组中与柱形图的时间相对应的图表上的柱形图编号。应将获得的数据写入绘图部分缓冲区,以便将当前图表中与计算部分缓冲区中柱形图开盘价相匹配的所有柱形图添加到绘图缓冲区的相应单元格中。

例如,五分钟图表周期上的一个柱对应于一分钟图表上的五个柱。一分钟图表的所有这五个柱都必须填充与它们在时间上对应的五分钟柱的值。类似的算法用于在较高的时间框架图上呈现较低时段的数据。在这种情况下,来自计算部分缓冲器的单元格的所有柱,对应于较高TF图上的柱的时间,被绘制在绘制缓冲区的一个柱上。

当然,读数可能并不精确,因为最终该柱将仅代表最后一个较低TF柱的数据,该数据与相应较高TF柱的时间相匹配。这里的一切都取决于从较低时间段的计算部分缓冲区接收数据的方向,最后接收的数据将绘制在较高时间段图表的柱形图上。

CopyBuffer()函数从计算的指标的缓冲区中获取数据:

函数将指定数量的指定指标缓冲区的数据接收到“buffer”数组中。

复制数据的元素(具有索引buffer_num的指标缓冲区)从开始位置开始计数,从现在到过去,也就是说,等于0的开始位置表示当前柱(当前柱的指标值)。

如果事先不知道要复制的数据量,建议使用动态数组作为目标数组缓冲区,因为CopyBuffer()会尝试将接收数组的大小分配到复制的数据的量。如果接收数组缓冲区是一个指标缓冲区(之前由SetIndexBufer()函数分配的用于存储指标值的数组),则允许部分复制。

如果需要将指标值部分复制到另一个数组(而不是指标缓冲区),则应使用一个中间数组,将所需数量复制到该数组中。从这个中间数组中,逐个成员将所需数量的值复制到接收数组的所需位置。

如果需要复制预定量的数据,建议使用静态分配的缓冲区,以避免不必要的内存重新分配。

接收数组的属性,即as_series=true或as_series=false,将被忽略:在复制过程中,最旧的元素将被复制到为数组分配的物理内存的开头。有三种函数选项。

按根据初始位置和所需元素的数量的访问

int  CopyBuffer(
   int       indicator_handle,     // indicator handle
   int       buffer_num,           // indicator buffer index
   int       start_pos,            // starting point 
   int       count,                // amount to copy
   double    buffer[]              // array the data to be copied to
   );

按初始日期和所需元素数量访问

int  CopyBuffer(
   int       indicator_handle,     // indicator handle
   int       buffer_num,           // indicator buffer index
   datetime  start_time,           // starting date
   int       count,                // amount to copy
   double    buffer[]              // array the data to be copied to
   );

按所需时间间隔的初始日期和最终日期访问

int  CopyBuffer(
   int       indicator_handle,     // indicator handle
   int       buffer_num,           // indicator buffer index
   datetime  start_time,           // starting date
   datetime  stop_time,            // end date
   double    buffer[]              // array the data to be copied to
   );

参数

indicator_handle

[in] 通过相关指标函数获得的指标句柄。

buffer_num

[in] 指标缓冲区的编号。

start_pos

[in] 复制的第一个元素的索引。

count

[in] 复制的元素数。

start_time

[in] 第一个元素对应的柱的时间。

stop_time

[in] 最后一个元素对应的柱的时间。

buffer[]

[out] double类型的数组。

返回值

复制的数组元素数,如果出现错误,则为-1。

注意

当从指标请求数据时,如果请求的时间序列尚未构建或应该从服务器下载,则函数会立即返回-1。在这种情况下,会启动所需数据的下载或构建。

当从EA或脚本请求数据时,如果终端在本地没有适当的数据,则会启动从服务器的下载。另外,如果可以从本地历史构建数据,但尚未准备好,则开始构建必要的时间序列。该函数返回超时到期时准备好的数量。

我们将使用该函数的第一个版本,即基于起始位置(y为循环索引)和所需元素数量的访问。

对象结构如下:

  1. 多交易品种、多周期指标的基类,包含所有指标通用的主要函数;
  2. 从基本对象派生的类,这些类按其类型描述每个指标;
  3. 指标的集合–您可以使用此类创建任何指标并将其添加到集合中。该类将为用户提供创建指标和从中接收数据的所有工具。

使用非当前图表中的数据时,为了避免时间序列被释放,您应该至少每两分钟访问一次此时间序列。在这种情况下,时间序列将被“保留”,这将加快对它的访问(不需要每次都等待数据同步)。在类构造函数中,我们将执行构建指标的时间序列的第一次调用。这将允许我们开始下载时间序列(如果无法在本地访问)。然后,每隔90秒,在基类计时器中,我们将访问时间序列来保存它。


资源高效计算

为了计算和显示指标,我们需要资源高效的计算。第一次启动时,将计算所有历史数据的指标,然后将计算所有后续分时的一个或两个柱形。

OnCalculate()处理函数具有预定义的常量变量,这些变量存储(时间序列或数组的)输入数据大小以及在上一次OnCalculate()调用 期间计算的数据量:

基于数据数组的计算

int  OnCalculate(
   const int        rates_total,       // price[] array size
   const int        prev_calculated,   // number of processed bars at the previous call
   const int        begin,             // index number in the price[] array meaningful data starts from
   const double&    price[]            // array of values for calculation
   );

基于当前时间段时间序列的计算

int  OnCalculate(
   const int        rates_total,       // size of input timeseries
   const int        prev_calculated,   // number of processed bars at the previous call
   const datetime&  time{},            // Time array
   const double&    open[],            // Open array
   const double&    high[],            // High array
   const double&    low[],             // Low array
   const double&    close[],           // Close array
   const long&      tick_volume[],     // Tick Volume array
   const long&      volume[],          // Real Volume array
   const int&       spread[]           // Spread array
   );

这种数据的存在使得能够快速、有效地计算指标。例如,让我们考虑“limit”值的计算:

//--- Number of bars for calculation
   int limit=rates_total-prev_calculated;
//--- If limit > 1, then this is the first calculation or change in the history
   if(limit>1)
     {
      //--- specify all the available history for calculation
      limit=rates_total-1;
      //--- If the indicator has any buffers, initialize them here with empty values set for these buffers
     }

当指标首次启动时,我们有时间序列大小(rates_total)和上次调用的计算数据量(prev_calculated)。由于尚未计算指标,因此在第一次启动时先前计算的柱的值为零。因此,“limit”值将大于1(等于可用柱数减去零)。使用此值,我们将“limit”指定为rates_total-1——用于计算的整个可用历史。在这种情况下,我们还需要首先从指标缓冲区中删除所有先前绘制的数据,例如:

ArrayInitialize(Buffer,EMPTY_VALUE);

之后,将在主循环中计算整个历史,该循环从“limit”到零(包括零)运行:

   for(int i=limit;i>=0;i--)
     {
      // Calculating indicator at each bar of the loop (i)
     }  

请注意,使用此计算方法,计算中使用的所有数组和指标缓冲区本身都必须具有索引为时间序列的标志:

ArraySetAsSeries(array,true);

如果计算出的“limit”等于1,则这意味着图表上打开了一个新的柱:指标将在从1到0(包括1到0)的循环中计算时间序列的第一个和零个柱。

如果计算的“limit”等于0,则这意味着对当前分时的操作:指标将仅计算从0到0(包括0)的循环中时间序列的第零柱。

如果您需要从零柱深入历史数据进行计算(以免扩展时间序列数组和缓冲区),则循环将反转:

   int i=fmax(0, prev_calculated-1);
   for(;i<rates_total;i++)
     {
      // Calculating indicator at each bar of the loop (i)
     }   

在当前图表上运行资源高效计算是非常容易的。但是,如果您需要的不是当前图表中的数据,该怎么办?何时从计算部分复制整个数据数组,何时仅复制最后一个或两个条形图?

这里我们将使用函数Bars()BarsCalculated()。这些类似于预定义的常量指标变量rates_total和prev_calculated。它们返回指定交易品种/周期的柱数以及指标计算的数据量。由于指标是为其创建时刻指定的交易品种/周期构建的,因此计算的数据量也指的是该交易品种/周期。我们通过它的句柄来获取指标。

基于我们可以计算任何交易品种/周期需要复制多少个柱(以免在每个分时处复制整个数组)的事实,我们将在指标基类中创建与当前交易品种/周期完全相同的结构:

limit=rates_total-prev_calculated;

但是变量“limit”、“rates_total”和“prev_calculated”将是类的私有成员,并将从Bars()BarsCalculated()函数接收值。“limit”值将在每个分时上计算,如果为零,则仅复制最后两个数据柱(当前和以前的数据柱)的数据。如果“limit”等于1,则这意味着在指标交易品种/周期上打开一个新的柱,您需要将数组增加1,然后从指标缓冲区复制数据-也是两个。当“limit”大于1时,整个数组将从指标的计算部分缓冲区复制到类的接收数组缓冲区,因为这被认为是第一次启动或历史上有了更改。

此逻辑适用于交易日,即分时到达的时间。

假期需要一种不同的方式。这是一个孤立的案例。这里没有分时,Bars()函数经常返回零,并且不记录计算的指标柱数,即也为零。如果用于计算的源数据中有任何错误,则指标应返回零。这意味着它将等待到下一个分时,并尝试再次计算。但在假期,除了第一次启动外,没有其他活动。

在第一次启动时,指标将首先清除缓冲区数组,然后进行计算。但是,由于计算数据不足,或者仅仅因为prev_calculated将返回零值,计算可能会失败。指标将退出OnCalculate(),再次返回零。因此,如果您通过右键单击菜单更新图表(这被视为分时模拟),指标将再次看到它是错误计算的,并将再次初始化缓冲区,考虑到这是第一次启动。这种行为将继续,并且刚刚在图表上呈现的绘图缓冲区数据将不断被擦除。不过有一个解决方案。

如果在第一次启动时无法立即计算指标,那么您可以等待20-30秒,等待下一个分时,并以编程方式模拟分时。这可以使用ChartSetSymbolPeriod()函数来完成。如果调用它并指定当前图表交易品种/周期,将会重新计算图表上运行的指标。因此,即使没有分时,也可以计算图表上的指标。

二十秒的等待足以加载指标交易品种/周期所需的历史记录并进行计算。但我们需要一个标志,表明指标已经计算完毕(因为prev_calculated返回零),如果我们不设置计算成功标志,指标将再次清除其缓冲区。因此,我们可以通过简单地看到指标对象的计算柱数等于其交易品种/周期上的柱数来理解指标已经成功计算。如果Bars()返回零,那么我们可以用另一种方式找到所需交易品种/周期的可用柱数(不要忘记,我们谈论的是在当前图表上运行的另一个指标或 EA 交易中计算的多交易品种、多周期指标)。在SeriesInfoInteger()函数中,我们可以获得交易品种/周期的可用历史记录的开始和结束日期:

SeriesInfoInteger(symbol,period,SERIES_FIRSTDATE);      // The very first date for a period and symbol at the moment
SeriesInfoInteger(symbol,period,SERIES_LASTBAR_DATE);   // Time of opening the last bar for the period and symbol

根据这两个日期和时间序列图表周期,即使Bars(symbol,period)或SeriesInfoInteger(symbol,period,SERIES_BARS_COUNT)返回零,我们也可以轻松计算可用柱的数量。

在已经接收到所有数据并且已经正确地计算出指标之后,设置计算成功标志。在limit>1的条件下检查此标志(在这种情况下,需要使用“空”值初始化指示器缓冲区数组)。在第一次启动时,成功标志被重置,数组被初始化,并尝试计算非当前图表交易品种/周期上的指标。如果计算失败,请等待20秒(根据计时器),等待下一个分时。

如果是周末,则不会有分时,20秒后会发送一个命令,设置图表的当前交易品种和周期,以模拟分时。当重新启动计时器中的指标时(20秒后),数据应该已经加载,并且指标的计算应该没有错误。在这种情况下,将设置计算成功标志。下一次激活计时器时,会检查此标志,如果已设置,则不会模拟分时。有三种这样的尝试来绘制指标缓冲区。在三次尝试之后,成功标志被强制设置,并且模拟分时的尝试被停止。如果指示器无法计算三个模拟分时,则只能手动操作:使用右键菜单刷新,或者来回切换图表时间框架,以完成整个分时模拟过程,并再次加载数据。

这就是理论。让我们继续练习:创建多指标类。


MSTF 指标的基类

在终端文件夹\MQL5\Include\IndMSTF\中,让我们为CIndMSTF类创建一个新文件IndMSTF.mqh。类必须从标准库基本对象CObject继承。基本对象文件必须连接到创建的新类文件

//+------------------------------------------------------------------+
//|                                                      IndMSTF.mqh |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Object.mqh>
//+------------------------------------------------------------------+
//| Base class of the multi-symbol multi-period indicator            |
//+------------------------------------------------------------------+
class CIndMSTF : public CObject
  {
private:

protected:

public:
//--- Constructor/destructor
                     CIndMSTF();
                    ~CIndMSTF();
  };

在我们将方法添加到类的各个部分之前,让我们添加一些宏替换、枚举和指标缓冲区的结构。

我们将需要第二个计时器来跟踪两个时间段:

  • 90秒计时器,之后我们将转到不在当前交易品种/周期上计算的指标的时间序列;
  • 20秒计时器,我们将在周末模拟分时来绘制指标。
让我们输入宏替换来设置这两个时间段的等待时间:
//--- includes
#include <Object.mqh>
//--- defines
#define TIMER_COUNT_1   (90)  // Timer 1 size. Must not be more than two minutes (120)
#define TIMER_COUNT_2   (20)  // Timer 2 size. 值太小会迅速触发分时模拟,这在活跃的市场中是不可取的

客户端中的不同标准指标属于不同的类别。为了让我们按类别对创建的指标进行排序或创建与任何类别相关的指标列表,让我们写一个不同指标类别的枚举

//--- defines
#define TIMER_COUNT_1   (90)  // Timer 1 size. Must not be more than two minutes (120)
#define TIMER_COUNT_2   (20)  // Timer 2 size. Too small values quickly trigger tick emulation, which is not desirable in an active market
//--- enums
enum ENUM_IND_CATEGORY        // Indicator categories
  {
   IND_CATEGORY_NONE,         // Not set
   IND_CATEGORY_TREND,        // Trend
   IND_CATEGORY_OSCILLATOR,   // Oscillators
   IND_CATEGORY_VOLUME,       // Volume
   IND_CATEGORY_WILLIAMS,     // Bill Williams
   IND_CATEGORY_CUSTOM,       // Custom
  };

关于排序、搜索和筛选

每个指标对象都具有可用于查找所需指标的属性。为了理解指标是一致的,我们需要比较它们的关键属性:交易品种、图表周期和所有输入参数的值。如果所比较的属性中至少有一个值不同,则指标不相同。如果指标相等,则不会创建新的指标,但会返回一个指向先前创建的具有相同参数的指标的指针。这涉及指标集合。关于属性,我们需要创建一个枚举,该枚举将包含一些属性的常量,这些常量可以设置为成功创建的指标,然后可以用于查找该指标:

enum ENUM_COMPARE_MODE        // Comparison mode
  {
   // By default, the comparison mode is set to zero, which compares all properties
   COMPARE_MODE_HANDLE=1,     // Compare by handle
   COMPARE_MODE_SYMBOL,       // Compare by symbol
   COMPARE_MODE_TIMEFRAME,    // Compare by chart period
   COMPARE_MODE_ID,           // Compare by ID
   COMPARE_MODE_DESCRIPTION,  // Compare by custom description
   COMPARE_MODE_CATEGORY,     // Compare by category
  };

每个成功创建的指标都有一个句柄,通过该句柄可以访问它。这是分配给指标计算部分的唯一编号。创建的指标的句柄值从10开始,每个后续值增加1。

其余的属性不是唯一的,可能是不同指标所固有的。此处提供使用这些值进行搜索只是为了方便。例如,可以为指标设置唯一的描述,然后通过此描述引用它。

指标线状态的描述在前面关于指标的文章中已经讨论过。在这里,我们还将使用此枚举:

enum ENUM_LINE_STATE          // Indicator line state
  {
   LINE_STATE_NONE,           // Undefined
   LINE_STATE_UP,             // Upward
   LINE_STATE_DOWN,           // Downward
   LINE_STATE_TURN_UP,        // Upward reversal
   LINE_STATE_TURN_DOWN,      // Downward reversal
   LINE_STATE_STOP_UP,        // Upward stop
   LINE_STATE_STOP_DOWN,      // Downward stop
   LINE_STATE_ABOVE,          // Above value
   LINE_STATE_BELOW,          // Below value
   LINE_STATE_CROSS_UP,       // Upward value crossing
   LINE_STATE_CROSS_DOWN,     // Downward value crossing
   LINE_STATE_TOUCH_BELOW,    // Touching value from below
   LINE_STATE_TOUCH_ABOVE,    // Touching value from above
   LINE_STATE_EQUALS,         // Equal to value
  };

有关更多详细信息,请参阅关于振荡器指标的文章,在ATR指标参数部分

每个指标将使用错误代码发出其计算结果的信号:

enum ENUM_ERR_TYPE            // Indicator calculation error type
  {
   ERR_TYPE_NO_ERROR,         // No error
   ERR_TYPE_NO_CYNC,          // Data not synchronized
   ERR_TYPE_NO_DATA,          // Data not loaded
   ERR_TYPE_NO_CALC,          // Calculation not completed
  };

使用此代码,可以从外部确定处理错误所需的操作。

指标缓冲区

在这里,我们需要决定缓冲区属于哪里。

  1. 计算部分的缓冲区。当我们创建一个指标时,它是在内存中创建的。这是指标的计算部分。它有自己的缓冲区,由终端子系统管理。您可以使用句柄访问计算部分,该句柄在成功创建指标后返回。成功创建和计算的指标的缓冲区始终包含与计算指标的时间序列相对应的数据。该缓冲区中的数据的位置使得零索引对应于计算指标的时间序列的当前柱。
    要从指标计算部分的缓冲区复制数据,我们使用CopyBuffer()函数

  2. 指示器对象的缓冲区。每个创建的多指标类对象都将根据该指标的缓冲区数量拥有自己的缓冲区数组。来自计算部分缓冲区的数据将被放置在这些数组中。指标对象的缓冲区将在类对象内部进行管理,在类对象中对它们进行初始化,根据创建指标的时间序列的大小增加大小,并在每个新的分时上更新。当使用CopyBufer()将数据从计算部分缓冲区复制到指标对象数组时,数据将被排列,使当前柱位于数组的末尾(ArraySize()-1)。

  3. 指标绘图部分的缓冲区。每个指标对象都可以在EA交易和自定义指标中创建。在EA交易中创建多个指标时,为了计算指标,我们调用计算指标的方法,为了获得计算出的数据,我们访问所需的指标对象缓冲区索引。在自定义指标的情况下,我们还需要绘制图表上多个指标的数据。这就是为什么这里还有一个绘图缓冲区。此缓冲区指定为在自定义指标中绘图。它将显示存储在指标对象缓冲区中的数据。要在图表上显示线条,只需从计算指标的自定义指标中调用指标集合类的方法就足够了,然后,如果计算成功,则调用将指标对象的缓冲区数据放置在自定义指标的绘图缓冲区中的方法。

在指标对象中,一个缓冲区将由一个结构表示,该结构包含动态数组本身和该数组的控制方法:

//--- struct
struct SBuffer                // Structure of the indicator buffer
  {
   double            array[];    // Indicator buffer array
   double            init_value; // Initializing value
   int               shift;      // Horizontal shift of the buffer
   string            descript;   // Buffer description
   //--- (1) Sets, (2) returns the initializing value,
   void              SetInitValue(const double value) { init_value=value;                             }
   double            InitValue(void)                  { return init_value;                            }
   //--- (1) Sets, (2) returns the buffer offset
   void              SetShift(const int value)        { shift=value;                                  }
   int               Shift(void)                      { return shift;                                 }
//--- (1) Resizes the buffer array, (2) returns the size of the buffer array,
//--- (3) initializes the array with the set "empty" value
   bool              BuffResize(const int new_size)   { return(ArrayResize(array,new_size)==new_size);}
   uint              BufferSize(void)                 { return array.Size();                          }
   int               InitBuffer(void)                 { return ArrayInitialize(array,init_value);     }
  };

一些值是在外部设置的,例如,在创建指标时,需要保存在某个位置,以便以后知道这些值是什么。最简单的方法是将它们直接写入结构中。这就是我们在这里所做的:调用程序设置的“空”缓冲区值保存在init_value变量的缓冲区结构中。在创建指标的计算部分时设置的指标线的偏移也可以保存在这里,以便以后您可以在类对象内部了解它;它保存在“shift”变量中。缓冲区的描述也保存在此处。此描述是在创建指标的计算部分时自动设置的,与同一标准指标的缓冲区名称相对应。此描述稍后可以更改。

只有在初始化和计算期间记录指标线和错误的状态或在信息面板上显示时,才需要返回指标线状态和错误说明的函数

//+------------------------------------------------------------------+
//| Return the indicator line state description                      |
//+------------------------------------------------------------------+
string BufferLineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_NONE       :  return "None";
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_BELOW      :  return "Below level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }
//+------------------------------------------------------------------+
//| Return error description in indicator calculation                |
//+------------------------------------------------------------------+
string TypeErrorcDescription(ENUM_ERR_TYPE error_type)
  {
   switch(error_type)
     {
      case ERR_TYPE_NO_CYNC   :  return "Data is not synchronized";
      case ERR_TYPE_NO_DATA   :  return "Data not loaded";
      case ERR_TYPE_NO_CALC   :  return "Calculation not completed";
      default                 :  return "No error";
     }
  }

所需的准备工作已经完成。让我们转到多交易品种多周期指标的对象类。

让我们在类的主体中写下类工作所需的所有私有、受保护和公共变量和方法,然后考虑它们的目的和实现:

//+------------------------------------------------------------------+
//| Base class of the multi-symbol multi-period indicator            |
//+------------------------------------------------------------------+
class CIndMSTF : public CObject
  {
private:
   ENUM_PROGRAM_TYPE m_program;           // Program type
   ENUM_INDICATOR    m_type;              // Indicator type
   ENUM_TIMEFRAMES   m_timeframe;         // Chart timeframe
   string            m_symbol;            // Chart symbol
   int               m_handle;            // Indicator handle
   int               m_id;                // Identifier
   bool              m_success;           // Successful calculation flag
   ENUM_ERR_TYPE     m_type_err;          // Calculation error type
   string            m_description;       // Custom description of the indicator
   string            m_name;              // Indicator name
   string            m_parameters;        // Description of indicator parameters

protected:
   ENUM_IND_CATEGORY m_category;          // Indicator category
   MqlParam          m_param[];           // Array of indicator parameters
   string            m_title;             // Title (indicator name + description of parameters)
   SBuffer           m_buffers[];         // Indicator buffers
   int               m_digits;            // Digits in indicator values
   int               m_limit;             // Number of bars required to calculate the indicator on the current tick
   int               m_rates_total;       // Number of available bars for indicator calculation
   int               m_prev_calculated;   // Number of calculated bars on the previous indicator call
   
//--- (1) Sets indicator name, (2) description of parameters
   void              SetName(const string name)                      { this.m_name=name;           }
   void              SetParameters(const string str)                 { this.m_parameters=str;      }
   
//--- Resizes the (1) specified, (2) all indicator buffers
   bool              BufferResize(const uint buffer_num,const int new_buff_size);
   bool              BuffersResize(const int new_buff_size);
//--- Initializes the (1) specified, (2) all indicator buffers
   bool              BufferInitialize(const uint buffer_num,const int new_buff_size);
   bool              BuffersInitialize(const int new_buff_size);
   
//--- Returns the flag indicating equality of the structure of one parameter of two objects
   bool              IsEqualParameters(const MqlParam &this_param,const MqlParam &compared_param) const
                       {
                        if(this_param.type==compared_param.type                     && 
                           this_param.integer_value==compared_param.integer_value   && 
                           this_param.string_value==compared_param.string_value     && 
                           ::NormalizeDouble(this_param.double_value-compared_param.double_value,8)==0
                          ) return true;
                        return false;
                       }
//--- Return the result of comparison on one parameter of two objects
   int               CompareParams(const MqlParam &this_param,const MqlParam &compared_param)
                       {
                        if(this.IsEqualParameters(this_param,compared_param))
                           return 0;
                        else if(this_param.type>compared_param.type                 || 
                           this_param.integer_value>compared_param.integer_value    || 
                           this_param.string_value>compared_param.string_value      || 
                           this_param.double_value>compared_param.double_value
                          ) return 1;
                        else if(this_param.type<compared_param.type                 || 
                           this_param.integer_value<compared_param.integer_value    || 
                           this_param.string_value<compared_param.string_value      || 
                           this_param.double_value<compared_param.double_value
                          ) return -1;
                        else
                           return -1;
                       }
   
public:
//--- Creates the calculation part of the indicator, returns the handle
   int               CreateIndicator(void);
//--- (1) Calculates the indicator, (2) fills the passed buffer array (taking into account the chart period/symbol) with data from the indicator calculation buffer of this class
   bool              Calculate(void);
   bool              DataToBuffer(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const int limit,double &buffer[]);

//--- (1) Sets (2) returns the initializing value for the specified buffer
   void              SetBufferInitValue(const uint buffer_num,const double value);
   double            BufferInitValue(const uint buffer_num) const;

//--- (1) Sets (2) returns the offset value for the specified buffer
   void              SetBufferShift(const uint buffer_num,const int value);
   double            BufferShift(const uint buffer_num) const;

//--- Returns data of the specified buffer (1) as is, (2) relative to the specified symbol/timeframe,
//--- (3) amount of data in the specified buffer, (4) the state of the indicator line as it is in the calculation part buffer,
//--- (5) indicator line state taking into account the chart symbol/period, description of the line state (6) as is in the buffer (7) taking into account the chart symbol/period
   double            GetData(const uint buffer_num,const int index)           const;
   double            GetDataTo(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const int index) const;
   uint              DataTotal(const uint buffer_num)                         const;
   ENUM_LINE_STATE   BufferLineState(const uint buffer_num,const int index)   const;
   ENUM_LINE_STATE   BufferLineState(const string symbol_from,const ENUM_TIMEFRAMES timeframes_from,const uint buffer_num,const int index) const;
   ENUM_LINE_STATE   BufferLineStateRelative(const int buffer_num,const int index,const double level0,const double level1=EMPTY_VALUE);
   ENUM_LINE_STATE   BufferLineStateRelative(const string symbol_from,const ENUM_TIMEFRAMES timeframes_from,const int buffer_num,const int index,const double level0,const double level1=EMPTY_VALUE);

//--- Returns (1) success flag, (2) calculation error type
   bool              IsSuccess(void)                           const { return this.m_success;               }
   ENUM_ERR_TYPE     TypeError(void)                           const { return this.m_type_err;              }
   
//--- Sets (1) identifier, (2) Digits, (3) custom description, (4) description of the specified buffer
   void              SetID(const int id)                             { this.m_id=id;                        }
   void              SetDigits(const uint digits)                    { this.m_digits=(int)digits;           }
   void              SetDescription(const string descr)              { this.m_description=descr;            }
   void              SetBufferDescription(const uint buffer_num,const string descr);

//--- Sets the indexing of buffer arrays of the calculation part not as in the timeseries
   void              SetAsSeriesOff(void);
//--- Returns flag of whether the buffer is set as series, (2) historical data for symbol/period is synchronized
   bool              IsSeries(const uint buffer_num) const;
   bool              IsSynchronized(void) const
                       {
                        return (bool)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_SYNCHRONIZED);
                       }
   
//--- Returns (1) timeframe, (2) symbol, (3) name, (4) list of parameters, (5) handle, (6) Digits
//--- number of (7) buffers, (8) bars, (9) identifier, (10) description, (11) title, (12) category,
//--- (13) number of parameters, (14) program type, description of (15) category, (16) indicator buffer
   ENUM_TIMEFRAMES   Timeframe(void)                           const { return this.m_timeframe;             }
   string            Symbol(void)                              const { return this.m_symbol;                }
   string            Name(void)                                const { return this.m_name;                  }
   string            Parameters(void)                          const { return this.m_parameters;            }
   int               Handle(void)                              const { return this.m_handle;                }
   int               Digits(void)                              const { return this.m_digits;                }
   uint              BuffersTotal(void)                        const { return this.m_buffers.Size();        }
   uint              RatesTotal(void)                          const { return this.m_rates_total;           }
   int               ID(void)                                  const { return this.m_id;                    }
   string            Description(void)                         const { return this.m_description;           }
   string            Title(void)                               const { return this.m_title;                 }
   ENUM_IND_CATEGORY Category(void)                            const { return this.m_category;              }
   uint              ParamsTotal(void)                         const { return this.m_param.Size();          }
   ENUM_PROGRAM_TYPE Program(void)                             const { return this.m_program;               }
   string            CategoryDescription(void);
   string            BufferDescription(const uint buffer_num);

//--- Returns (1) structure of parameters by index from array, (2) flag of indicator program, (3) timeframe description
   MqlParam          GetMqlParam(const int index)              const { return this.m_param[index];          }
   bool              IsIndicator()                 const { return(this.Program()==PROGRAM_INDICATOR);       }
   string            TimeframeDescription(void)    const
                       {
                        return ::StringSubstr(::EnumToString(this.m_timeframe),7);
                       }
//--- Returns amount of calculated data
   int               Calculated(void)  const { return ::BarsCalculated(this.m_handle);                      }
   
//--- Virtual method returning the type of object (indicator)
   virtual int       Type(void)                                const { return this.m_type;                  }
//--- Virtual method for comparing two objects
   virtual int       Compare(const CObject *node,const int mode=0) const
                       {
                        const CIndMSTF *compared=node;
                        switch(mode)
                          {
                           case COMPARE_MODE_ID          : return(this.ID()>compared.ID()                   ? 1 : this.ID()<compared.ID()                   ? -1 : 0);
                           case COMPARE_MODE_HANDLE      : return(this.Handle()>compared.Handle()           ? 1 : this.Handle()<compared.Handle()           ? -1 : 0);
                           case COMPARE_MODE_CATEGORY    : return(this.Category()>compared.Category()       ? 1 : this.Category()<compared.Category()       ? -1 : 0);
                           case COMPARE_MODE_SYMBOL      : return(this.Symbol()>compared.Symbol()           ? 1 : this.Symbol()<compared.Symbol()           ? -1 : 0);
                           case COMPARE_MODE_TIMEFRAME   : return(this.Timeframe()>compared.Timeframe()     ? 1 : this.Timeframe()<compared.Timeframe()     ? -1 : 0);
                           case COMPARE_MODE_DESCRIPTION : return(this.Description()>compared.Description() ? 1 : this.Description()<compared.Description() ? -1 : 0);
                           //--- Equality of all object parameters
                           default                       : return(this.IsEqualIndicators(compared) ? 0 : -1);
                          }
                       }
//--- Returns the flag of equality of parameters of two indicator objects
   bool              IsEqualIndicators(const CIndMSTF *compared) const
                       {
                        if(this.Type()!=compared.Type() || this.ParamsTotal()!=compared.ParamsTotal())
                           return false;
                        bool res=true;
                        int total=(int)this.ParamsTotal();
                        for(int i=0;i<total;i++)
                           res &=this.IsEqualParameters(this.m_param[i],compared.GetMqlParam(i));
                        res &=(this.Timeframe()==compared.Timeframe());
                        res &=(this.Symbol()==compared.Symbol());
                        return res;
                       }
//--- Timer
   void OnTimer(void);
   
//--- Constructor/destructor
                     CIndMSTF(){}
                     CIndMSTF(const ENUM_INDICATOR type,const uint buffers,const string symbol,const ENUM_TIMEFRAMES timeframe);
                    ~CIndMSTF();
  };

类代码中对每个变量和方法的用途进行了注释。让我们考虑一下方法的实现。

类的构造函数:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CIndMSTF::CIndMSTF(const ENUM_INDICATOR type,const uint buffers,const string symbol,const ENUM_TIMEFRAMES timeframe)
  {
//--- Start the timer
   ::ResetLastError();
   if(!::EventSetTimer(1))
      ::PrintFormat("%s: EventSetTimer failed. Error %lu",__FUNCTION__,::GetLastError());
//--- Set properties to the values passed to the constructor or to default values
   this.m_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE);
   this.m_type=type;
   this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol);
   this.m_timeframe=(timeframe==PERIOD_CURRENT ? ::Period() : timeframe);
   this.m_handle=INVALID_HANDLE;
   this.m_digits=::Digits();
   this.m_success=true;
   this.m_type_err=ERR_TYPE_NO_ERROR;
//--- Set the size of the buffer structure array to the number of indicator buffers
   ::ResetLastError();
   if(::ArrayResize(this.m_buffers,buffers)!=buffers)
      ::PrintFormat("%s: Buffers ArrayResize failed. Error  %lu"__FUNCTION__,::GetLastError());
//--- Set the "empty" value for each buffer by default (can be changed it later)
   for(int i=0;i<(int)this.m_buffers.Size();i++)
      this.SetBufferInitValue(i,EMPTY_VALUE);
//--- Set initial values of variables involved in resource-efficient calculation of the indicator
   this.m_prev_calculated=0;
   this.m_limit=0;
   this.m_rates_total=::Bars(this.m_symbol,this.m_timeframe);
//--- If the indicator is calculated on non-current chart, request data from the required chart
//--- (the first access to data starts data pumping)
   datetime array[];
   if(symbol!=::Symbol() || timeframe!=::Period())
      ::CopyTime(this.m_symbol,this.m_timeframe,0,this.m_rates_total,array);
  }

指标的类型、缓冲区的数量、交易品种的名称和图表的时间框架被传递给类构造函数。接下来,设置变量的默认值,设置缓冲区数组的大小,并且给缓冲区数组以EMPTY_VALUE赋值进行初始化。如果指标对象是在非当前图表上计算的,那么在构造函数的末尾,我们调用一个函数,该函数开始从服务器下载计算指标的时间序列数据。

类的析构函数:

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CIndMSTF::~CIndMSTF()
  {
//--- Delete timer
   ::EventKillTimer();
//--- Release indicator handle
   ::ResetLastError();
   if(this.m_handle!=INVALID_HANDLE && !::IndicatorRelease(this.m_handle))
      ::PrintFormat("%s: %s, handle %ld IndicatorRelease failed. Error %ld",__FUNCTION__,this.Title(),m_handle,::GetLastError());
//--- Free up the memory of buffer arrays
   for(int i=0;i<(int)this.BuffersTotal();i++)
      ::ArrayFree(this.m_buffers[i].array);
  }

在类析构函数中,我们销毁计时器,释放指标句柄并释放缓冲区数组的内存。

计时器函数:

//+------------------------------------------------------------------+
//| Timer                                                            |
//+------------------------------------------------------------------+
void CIndMSTF::OnTimer(void)
  {
//--- If the indicator symbol and timeframe match those of the current chart, exit
   if(this.Symbol()==::Symbol() && this.Timeframe()==::Period())
      return;
//--- Declare time counter variable
   static int count1=0;
   static int count2=0;
//--- If the counter of timer 1 has reached the specified value,
   if(count1>=TIMER_COUNT_1)
     {
      //--- call the CopyTime function (timeseries hold) and reset the counter
      datetime array[1];
      ::CopyTime(this.m_symbol,this.m_timeframe,0,1,array);
      count1=0;
     }
//--- If the counter of timer 2 has reached the specified value
   if(count2>=TIMER_COUNT_2)
     {
      static int count=0;
      //--- if the previous indicator calculation failed, emulate a tick and print the relevant log
      if(!this.m_success)
        {
         if(::ChartSetSymbolPeriod(0,::Symbol(),::Period()))
           {
            count++;
            ::PrintFormat("%s::%s: Tick emulation. Attempt %ld of 3 ...",__FUNCTION__,this.Title(),count);
            if(count>2)
              {
               count=0;
               this.m_success=true;
              }
           }
        }
      //--- reset the counter
      count2=0;
     }
//--- Increase timer counters
   count1++;
   count2++;
  }

类计时器包含两个:一个用于维护指标的时间序列,另一个用于模拟周末的分时。如果使用当前图表交易品种/时间框架的数据计算指标,则不使用计时器。

指标对象本身不是指标,它只是指标计算部分的包装器,允许您管理它,从中接收数据并将数据传递给程序。在多指标对象中,我们需要创建指标本身。为此,我们使用创建指标的计算部分的方法,该方法返回创建过程中收到的句柄:

//+------------------------------------------------------------------+
//| Creates an indicator, returns a handle                           |
//+------------------------------------------------------------------+
int CIndMSTF::CreateIndicator(void)
  {
//--- Create indicator name to print to logs
   string name=::StringFormat("%s(%s,%s)",::StringSubstr(::EnumToString(this.m_type),4),this.m_symbol,this.TimeframeDescription());
//--- Create the calculation part of the indicator
   ::ResetLastError();
   this.m_handle=::IndicatorCreate(this.m_symbol,this.m_timeframe,this.m_type,this.m_param.Size(),this.m_param);
//--- If the calculation part is not created, log a message about the failed creation of the indicator
   if(this.m_handle==INVALID_HANDLE)
      ::PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,name,::GetLastError());
//--- If the indicator is created, set non-timeseries indexing of indicator arrays
   else
      this.SetAsSeriesOff();
//--- Return the handle of the created indicator or -1 if unsuccessful
   return this.m_handle;
  }

指标对象的缓冲区是我们上面讨论的缓冲区结构内的动态数组。应该更改它们的大小以适应不断增长的新数据量。有两种方法:一种是更改指定缓冲区数组的大小,另一种是同时更改指标对象的所有缓冲区的大小。

调整指定指标缓冲区大小的方法:

//+------------------------------------------------------------------+
//| Resize the specified indicator buffer                            |
//+------------------------------------------------------------------+
bool CIndMSTF::BufferResize(const uint buffer_num,const int new_buff_size)
  {
//--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to the log and return 'false'
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      return false;
     }
//--- Resize the buffer
   ::ResetLastError();
   bool res=this.m_buffers[buffer_num].BuffResize(new_buff_size);
//--- If unsuccessful, print a message to the log
   if(!res)
      ::PrintFormat("%s::%s: Buffer(%lu) resize failed. Error %lu",__FUNCTION__,this.Title(),buffer_num,::GetLastError());
//--- Return the result of resizing the buffer array
   return res;
  }

该方法在参数中接收缓冲区的编号,该缓冲区的数组应根据传递给该方法的值调整大小。如果缓冲区编号指定不正确,则会记录相关消息,并且该方法返回false
如果数组调整大小失败,则会在日志中打印一条消息。最终返回缓冲区数组大小调整结果

调整所有指示器缓冲区大小的方法

//+------------------------------------------------------------------+
//| Resize all indicator buffers                                     |
//+------------------------------------------------------------------+
bool CIndMSTF::BuffersResize(const int new_buff_size)
  {
//--- In a loop through all indicator buffers, add to the 'res' variable the resizing result of each next buffer
   bool res=true;
   int total=(int)this.BuffersTotal();
   for(int i=0;i<total;i++)
      res &=this.m_buffers[i].BuffResize(new_buff_size);
//--- Return the result of resizing all arrays of indicator buffers
   return res;
  }

这里,在遍历指标对象的所有缓冲区的循环中,调整下一个缓冲区数组大小的结果被添加到“res”变量中,该变量的值最终从该方法返回。

以类似的方式组织用于初始化指示符对象缓冲区的数组的方法。

初始化指定的指标缓冲区的方法:

//+------------------------------------------------------------------+
//| Initialize the specified indicator buffer                        |
//+------------------------------------------------------------------+
bool CIndMSTF::BufferInitialize(const uint buffer_num,const int new_buff_size)
  {
//--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to the log and return 'false'
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      return false;
     }
//--- resizing the buffer array
   bool res=this.BufferResize(buffer_num,new_buff_size);
//--- If successfully resized, initialize the buffer with the set initializing value
   if(res)
      this.m_buffers[buffer_num].InitBuffer();
//--- Return the result
   return res;
  }

这里还检查初始化缓冲区的编号的正确性。然后将缓冲区设置为新的大小,如果调整大小成功,则将数组初始化为此缓冲区设置的值。

初始化所有指示器缓冲区的方法:

//+------------------------------------------------------------------+
//| Initialize all indicator buffers                                 |
//+------------------------------------------------------------------+
bool CIndMSTF::BuffersInitialize(const int new_buff_size)
  {
//--- In a loop through all indicator buffers, add to the 'res' variable the resizing result of each next buffer
//--- If successfully resized, initialize the buffer with the set initializing value
   bool res=true;
   int total=(int)this.BuffersTotal();
   for(int i=0;i<total;i++)
     {
      res &=this.m_buffers[i].BuffResize(new_buff_size);
      if(res)
         this.m_buffers[i].InitBuffer();
     }
//--- Return the overall result
   return res;
  }

在遍历指标对象的所有缓冲区的循环中,调整下一个缓冲区数组大小的结果将添加到“res”变量中。成功调整大小后,将缓冲区初始化为为其设置的初始化值。该方法返回“res”变量的最终状态,如果至少有一个缓冲区未能初始化,该变量将具有false值。

用于设置和返回缓冲区值的其余方法与上面讨论的方法相同:

//+------------------------------------------------------------------+
//| Set the initializing value for the specified buffer              |
//+------------------------------------------------------------------+
void CIndMSTF::SetBufferInitValue(const uint buffer_num,const double value)
  {
//--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to the log and exit
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      return;
     }
//--- Set a new initializing value for the specified buffer
   this.m_buffers[buffer_num].SetInitValue(value);
  }
//+------------------------------------------------------------------+
//| Return the initialization value of the specified buffer          |
//+------------------------------------------------------------------+
double CIndMSTF::BufferInitValue(const uint buffer_num) const
  {
//--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to log
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      //--- If the indicator has buffers, return the initializing value of the first one, otherwise EMPTY_VALUE
      return(this.BuffersTotal()>0 ? this.BufferInitValue(0) : EMPTY_VALUE);
     }
//--- Return the initializing value of the requested buffer
   return this.m_buffers[buffer_num].InitValue();
  }
//+------------------------------------------------------------------+
//| Sets the offset value for the specified buffer                   |
//+------------------------------------------------------------------+
void CIndMSTF::SetBufferShift(const uint buffer_num,const int value)
  {
//--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to the log and exit
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      return;
     }
//--- Set the offset value for the buffer
   this.m_buffers[buffer_num].SetShift(value);
  }
//+------------------------------------------------------------------+
//| Return the offset value of the specified buffer                  |
//+------------------------------------------------------------------+
double CIndMSTF::BufferShift(const uint buffer_num) const
  {
//--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to log
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      //--- If the indicator has buffers, return the shift value of the very first one, otherwise 0
      return(this.BuffersTotal()>0 ? this.m_buffers[0].Shift() : 0);
     }
//--- Return the offset value of the requested buffer
   return this.m_buffers[buffer_num].Shift();
  }

这些方法的逻辑在代码的注释中有详细描述。

计算指标数据的方法。计算指标(其计算部分)具有一个包含所有计算指标数据的缓冲区。我们需要将数据从计算部分缓冲区复制到指标对象的缓冲区数组中。对于此操作,我们需要组织一个资源高效的计算:我们将仅在第一次启动时或历史数据发生变化时复制整个缓冲区。

我们在上面讨论了这样一个计算,它应该被应用在指标对象的计算方法中。如果出现错误,该方法将返回false,并且调用程序将在下一个分时到达之前响应退出处理程序(EA交易中的OnTick和指标中的OnCalculate)。需要该方法返回的错误将被视为下载历史数据的开始、历史下载不完整、指标计算不完整以及将数据从计算部分缓冲区复制到指标对象缓冲区的错误。该方法将把错误代码写入变量,以便调用方能够正确读取并处理它。

使用计算部件缓冲区中的数据填充指标对象缓冲区的方法:

//+------------------------------------------------------------------+
//| Fill object buffers with data from the calculation part buffer   |
//+------------------------------------------------------------------+
bool CIndMSTF::Calculate(void)
  {
//--- Set the success flag to true, and the error type to no error
   this.m_success=true;
   this.m_type_err=ERR_TYPE_NO_ERROR;
//--- If the data is not yet synchronized with the trade server,
   if(!this.IsSynchronized())
     {
      //--- Log a message about non-synchronized data,
      ::PrintFormat("%s::%s: Waiting for data to sync...",__FUNCTION__,this.Title());
      //--- set the error type, add 'false' to the error flag and return 'false'
      this.m_type_err=ERR_TYPE_NO_CYNC;
      this.m_success &=false;
      return false;
     }
//--- If the Calculated method returned -1, this means the start of data downloading
   if(this.Calculated()==WRONG_VALUE)
     {
      //--- Log a message about the start of data downloading,
      ::PrintFormat("%s::%s: Start downloading data by %s/%s. Waiting for the next tick...",__FUNCTION__,this.Title(),this.m_symbol,this.TimeframeDescription());
      //--- set the error type, add 'false' to the error flag and return 'false'
      this.m_type_err=ERR_TYPE_NO_DATA;
      this.m_success &=false;
      return false;
     }
//--- If the Calculated method returned 0, this means that the indicator has not yet been calculated
   if(this.Calculated()==0)
     {
      //--- Log a message about waiting for the indicator to be calculated,
      ::PrintFormat("%s::%s: Waiting for a new tick and when the indicator will be calculated...",__FUNCTION__,this.Title());
      //--- set the error type, add 'false' to the error flag and return 'false'
      this.m_type_err=ERR_TYPE_NO_CALC;
      this.m_success &=false;
      return false;
     }
//--- Get the number of data bars for the indicator symbol/period
   int bars=::Bars(this.m_symbol,this.m_timeframe);
//--- If the Bars function returned a zero value, which often happens on weekends, calculate the available number of bars
   if(bars==0)
     {
      //--- Get the date of the very first available bar in history for the symbol/period
      datetime firstdate=(datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_FIRSTDATE);
      //--- Get the date of the last (current) bar in history for the symbol/period
      datetime lastdate=(datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_LASTBAR_DATE);
      //--- Calculate the number of bars between the first and last dates of history
      int sec=::PeriodSeconds(this.m_timeframe);
      ulong date_bars=(((ulong)lastdate-(ulong)firstdate)/(sec>0 ? sec : 1))+1;
      //--- Write to the 'bars' variable the smaller value of the calculated number of bars and the maximum number of bars available in the terminal
      bars=(int)fmin(date_bars,::TerminalInfoInteger(TERMINAL_MAXBARS));
     }
//--- Write the resulting number of available bars to m_rates_total
   if(this.m_rates_total!=bars)
      this.m_rates_total=bars;
//--- If the number of available bars is received, and it is 2 or less,
   if(this.m_rates_total>=0 && this.m_rates_total<3)
     {
      //--- Log a message about the number of available bars being too small
      ::PrintFormat("%s::%s: Not enough data for calculation: %ld bars. Waiting for the next tick...",__FUNCTION__,this.Title(),this.m_rates_total);
      //--- set the error type, add 'false' to the error flag and return 'false'
      this.m_type_err=ERR_TYPE_NO_DATA;
      this.m_success &=false;
      return false;
     }

//--- Calculate the number of bars required to calculate the indicator
//--- Either the entire available history, or 1 when a new bar opens, or 0 on the current tick
   this.m_limit=this.m_rates_total-this.m_prev_calculated;
   this.m_prev_calculated=this.Calculated();

//--- Declare an array of size 2 to receive data into it from the indicator's calculation part buffer
//--- We always get two bars: previous and current
   double array[2];
//--- Get the number of indicator buffers
   int total=(int)this.BuffersTotal();
//--- If the calculated m_limit is greater than 1, it means either the first launch or changes in historical data
//--- In this case, a complete recalculation of the indicator is necessary
   if(this.m_limit>1)
     {
      //--- In a loop over the number of indicator buffers
      for(int i=0;i<total;i++)
        {
         //--- resize the indicator buffer array and initialize it to the "empty" value set for this buffer
         this.BufferInitialize(i,this.m_rates_total);
         ::ResetLastError();
         //--- Copy all available historical data from indicator's calculation part array to buffer array of indicator object
         int copied=::CopyBuffer(this.m_handle,i,-this.m_buffers[i].Shift(),this.m_rates_total,this.m_buffers[i].array);
         //--- If not all data is copied
         if(copied!=this.m_rates_total)
           {
            //--- If CopyBuffer returned -1, this means the start of historical data downloading
            //--- print a message about this to the log
            if(copied==WRONG_VALUE)
               ::PrintFormat("%s::%s: Start downloading data by %s/%s. Waiting for the next tick...",__FUNCTION__,this.Title(),this.m_symbol,this.TimeframeDescription());
            //--- In any other case, not all data has been copied yet
            //--- print a message about this to the log
            else
               ::PrintFormat("%s::%s: Not all data was copied. Data available: %lu, total copied: %ld",__FUNCTION__,this.Title(),this.m_rates_total,copied);
            //--- Write the absence of data to the error type
            this.m_type_err=ERR_TYPE_NO_DATA;
            //--- Add 'false' to the result and return 'false' to exit the method and wait for the next tick
            this.m_success &=false;
            return false;
           }
        }
      //--- If we exited the loop of copying all indicator buffers, then everything was successful - return 'true'
      return true;
     }
//--- If calculated m_limit is less than or equal to 1, this means either opening of a new bar (m_limit==1) or current tick (m_limit==0)
//--- In this case, it is necessary to calculate two bars - the first and the current
   if(this.m_limit<=1)
     {
      //--- In a loop over the number of indicator buffers
      for(int i=0;i<total;i++)
        {
         //--- If this is the opening of a new bar and resizing the indicator buffer failed,
         if(this.m_limit==1 && !this.BufferResize(i,this.m_rates_total))
           {
            //--- add 'false' to the m_success variable and return 'false'
            //--- Here, an error message will be printed to log from the BufferResize method
            this.m_success &=false;
            return false;
           }
         //--- If failed to copy two bars from the indicator's calculation part buffer,
         ::ResetLastError();
         if(::CopyBuffer(this.m_handle,i,-this.m_buffers[i].Shift(),2,array)!=2)
           {
            //--- report this via the log, add 'false' to the m_success variable and return 'false'
            ::PrintFormat("%s::%s: CopyBuffer(%lu) failed. Error %lu",__FUNCTION__,this.Title(),i,::GetLastError());
            this.m_success &=false;
            return false;
           }
         //--- If got here, it means copying was successful -
         //--- copy data from array[], into which the last two bars were copied, to the indicator object buffer
         this.m_buffers[i].array[this.DataTotal(i)-1]=array[1];
         this.m_buffers[i].array[this.DataTotal(i)-2]=array[0];
        }
      //--- Success
      return true;
     }
//--- Undefined 'limit' option - return 'false'
   return false;
  }

在每个代码块的注释中详细描述了该方法的整个逻辑。该方法从程序中调用,如果返回false,则在下一次分时之前退出OnTick或OnCalculate。

如果该方法完成时没有出现错误,那么指标对象的缓冲区将包含可供使用的数据。可以使用下面将讨论的方法来访问这些数据。

指标具有特殊的方法,用于将由该方法填充的指标对象缓冲区中的数据输出到指标绘图缓冲区中。这些方法以将数据写入指标对象缓冲区的相同形式,或考虑图表交易品种/周期,将数据输出到绘图缓冲区。这些方法可用于在当前图表的指标对象中显示计算数据。

用类缓冲区中的数据填充传递的数组的方法:

//+------------------------------------------------------------------+
//| Fill the passed array with data from the class buffer            |
//+------------------------------------------------------------------+
bool CIndMSTF::DataToBuffer(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const int limit,double &buffer[])
  {
//--- Set the success flag
   this.m_success=true;
//--- Get the indexing direction of the buffer array passed to the method and,
//--- if non-timeseries indexing, set timeseries indexing
   bool as_series=::ArrayGetAsSeries(buffer);
   if(!as_series)
      ::ArraySetAsSeries(buffer,true);
//--- Set the symbol name and timeframe value passed to the method
   string symbol=(symbol_to=="" || symbol_to==NULL ? ::Symbol() : symbol_to);
   ENUM_TIMEFRAMES timeframe=(timeframe_to==PERIOD_CURRENT ? ::Period() : timeframe_to);
   datetime array[2];
//--- If this is the first launch or history changes, initialize the buffer array passed to the method
   if(limit>1 && this.m_limit>1)
     {
      ::PrintFormat("%s::%s First start, or historical data has been changed. Initialize Buffer(%lu)",__FUNCTION__,this.Title(),buffer_num);
      ::ArrayInitialize(buffer,this.BufferInitValue(buffer_num));
     }
//--- Set the value of the loop counter (no more than the maximum number of bars in the terminal on the chart)
   int count=(limit<=1 ? 2 : ::fmin(::TerminalInfoInteger(TERMINAL_MAXBARS),limit));
//--- In a loop from the zero bar to the value of the loop counter
   for(int i=0;i<count;i++)
     {
      //--- If the chart timeframe matches the class object timeframe, fill the buffer directly from the class object array
      if(timeframe==::Period() && this.m_timeframe==::Period())
         buffer[i]=this.GetData(buffer_num,i);
      //--- Otherwise, if the chart timeframe is not equal to the timeframe of the class object
      else
        {
         //--- Find out which time of this class the bar of the current chart timeframe, corresponding to the loop index, belongs to
         ::ResetLastError();
         if(::CopyTime(symbol,timeframe,i,2,array)!=2)
           {
            //--- If there is no data in the terminal, move on
            if(::GetLastError()==4401)
               continue;
            //--- Error in obtaining existing data - return false
            this.m_success &=false;
            return false;
           }
         //--- Using time of bar of current chart timeframe, find corresponding index of bar of class object's chart period
         ::ResetLastError();
         int bar=::iBarShift(this.m_symbol,this.m_timeframe,array[0]);
         if(bar==WRONG_VALUE)
           {
            this.m_success &=false;
            continue;
           }
         //--- If this is historical data (not the first or zero bar) -
         //--- in the indicator buffer at the loop index, write the value obtained from the calculation part buffer
         if(i>1)
            buffer[i]=this.GetData(buffer_num,bar);
         //--- If this is the current (zero) or previous (first) bar
         else
           {
            //--- Get the time of bars 0 and 1 by symbol/timeframe of the class object
            if(::CopyTime(this.m_symbol,this.m_timeframe,0,2,array)!=2)
              {
               this.m_success &=false;
               return false;
              }
            //--- Using time, get indexes of current and previous bars on the chart whose symbol/period was passed to method
            int bar0=::iBarShift(symbol,timeframe,array[1]);
            int bar1=::iBarShift(symbol,timeframe,array[0]);
            if(bar0==WRONG_VALUE || bar1==WRONG_VALUE)
              {
               this.m_success &=false;
               return false;
              }
            //--- If the chart timeframe is lower than the timeframe of the class object,
            if(timeframe<this.m_timeframe)
              {
               //--- in a loop from bar with smaller time to current chart bar, fill the buffer with data from the last 2 cells of the indicator buffer array
               for(int j=bar1;j>=0;j--)
                  buffer[j]=this.GetData(buffer_num,(j>bar0 ? 1 : 0));
              }
            //--- If the chart timeframe is higher than the timeframe of the class object,
            else
              {
               //--- Get the time of the current and previous bars by symbol/timeframe of the current chart
               if(::CopyTime(symbol,timeframe,0,2,array)!=2)
                 {
                  this.m_success &=false;
                  return false;
                 }
               //--- Using time, get indexes of bars in indicator's calculation part buffer, corresponding to time of current and previous bars on the chart
               int bar0=::iBarShift(this.m_symbol,this.m_timeframe,array[1]);
               int bar1=::iBarShift(this.m_symbol,this.m_timeframe,array[0]);
               //--- Write into indicator buffer, at indexes 1 and 0, values from corresponding indexes of calculation part buffer
               buffer[1]=this.GetData(buffer_num,bar1);
               buffer[0]=this.GetData(buffer_num,bar0);
              } 
           }
        }
     }
//--- Set initial indexing of the buffer array passed to the method
   ::ArraySetAsSeries(buffer,as_series);
//--- Successful
   return true;
  }

方法逻辑在其列表中进行了详细描述。该方法的思想是正确计算当前图表的柱形,这些柱需要填充在不同时间段上计算的指标缓冲区数组中的数据。传递给该方法的最后一个参数是自定义指标绘图缓冲区的数组,该数组应呈现在不同交易品种/周期上计算的指标。

按如下方式返回指定缓冲区数据的方法:

//+------------------------------------------------------------------+
//| Return the data of the specified buffer as is                    |
//+------------------------------------------------------------------+
double CIndMSTF::GetData(const uint buffer_num,const int index) const
  {
//--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to log
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      //--- If the indicator has buffers, return "empty" value of the first one, otherwise EMPTY_VALUE
      return(this.BuffersTotal()>0 ? this.BufferInitValue(0) : EMPTY_VALUE);
     }
//--- If an incorrect index is specified, return the "empty" value of the specified buffer
   if(index<0 || index>(int)this.DataTotal(buffer_num)-1)
      return this.BufferInitValue(buffer_num);
//--- Calculate the real index in the buffer array and return the value at this index
   int n=int(this.DataTotal(buffer_num)-1-index);
   return this.m_buffers[buffer_num].array[n];
  }

该方法只是在指定索引处从指标对象缓冲区返回数据。

返回指定交易品种/时间框架的指定缓冲区数据的方法:

//+-------------------------------------------------------------------+
//| Returns data from specified buffer for specified symbol/timeframe |
//+-------------------------------------------------------------------+
double CIndMSTF::GetDataTo(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const int index) const
  {
//--- If current symbol/period of the chart is specified
   if(timeframe_to==::Period() && this.m_timeframe==::Period() && symbol_to==::Symbol() && this.m_symbol==::Symbol())
      return this.GetData(buffer_num,index);
//--- Find out which time of this class the current chart timeframe's bar, corresponding to the loop index, belongs to
   datetime array[];
   if(::CopyTime(symbol_to,timeframe_to,index,1,array)!=1)
      return this.BufferInitValue(buffer_num);
//--- Using time of bar of current chart timeframe, find corresponding bar index of bar this class chart period
   int bar=iBarShift(this.m_symbol,this.m_timeframe,array[0]);
//--- If the bar is not found, return the "empty" value set for the buffer
   if(bar==WRONG_VALUE)
      return this.BufferInitValue(buffer_num);
//--- Return value from the indicator object buffer at the found index
   return this.GetData(buffer_num,bar);
  }

该方法查找时间序列的柱的索引,在该时间序列上计算与传递给该方法的图表交易品种/周期相对应的指标,并在找到的索引处从指标对象缓冲区返回数据。

返回指标线状态的方法:

//+------------------------------------------------------------------+
//| Return the state of the indicator line as is                     |
//+------------------------------------------------------------------+
ENUM_LINE_STATE CIndMSTF::BufferLineState(const uint buffer_num,const int index) const
  {
//--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
   const double value0=this.GetData(buffer_num,index);
   const double value1=this.GetData(buffer_num,index+1);
   const double value2=this.GetData(buffer_num,index+2);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Line upward reversal (value2>value1 && value0>value1)
   if(::NormalizeDouble(value2-value1,this.m_digits)>0 && ::NormalizeDouble(value0-value1,this.m_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Line upward direction (value2<=value1 && value0>value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)<=0 && ::NormalizeDouble(value0-value1,this.m_digits)>0)
      return LINE_STATE_UP;
//--- Line upward stop (value2<=value1 && value0==value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)<=0 && ::NormalizeDouble(value0-value1,this.m_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Line downward reversal (value2<value1 && value0<value1)
   if(::NormalizeDouble(value2-value1,this.m_digits)<0 && ::NormalizeDouble(value0-value1,this.m_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Line downward direction (value2>=value1 && value0<value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)>=0 && ::NormalizeDouble(value0-value1,this.m_digits)<0)
      return LINE_STATE_DOWN;
//--- Line downward stop (value2>=value1 && value0==value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)>=0 && ::NormalizeDouble(value0-value1,this.m_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Undefined state
   return LINE_STATE_NONE;
  }

该方法根据指标对象缓冲区中的数据确定指标对象的线状态,并返回找到的值。

返回特定交易品种/周期的指标状态的方法:

//+------------------------------------------------------------------+
//| Return indicator line state for the specific symbol/period       |
//+------------------------------------------------------------------+
ENUM_LINE_STATE CIndMSTF::BufferLineState(const string symbol_from,const ENUM_TIMEFRAMES timeframes_from,const uint buffer_num,const int index) const
  {
//--- Determine the chart symbol/period passed to the method
   string symbol=(symbol_from=="" || symbol_from==NULL ? ::Symbol() : symbol_from);
   ENUM_TIMEFRAMES timeframe=(timeframes_from==PERIOD_CURRENT ? ::Period() : timeframes_from);
//--- If we get data from symbol/period equal to current chart, return state from the buffer "as is"
   if(symbol==::Symbol() && symbol==this.m_symbol && timeframe==::Period() && timeframe==this.m_timeframe)
      return this.BufferLineState(buffer_num,index);
//--- Declare variables to search for the required bars on the current chart
   datetime array[1];
   int      bar0=WRONG_VALUE;
   int      bar1=WRONG_VALUE;
   int      bar2=WRONG_VALUE;

//--- Get the time of the first bar on the chart
   ::ResetLastError();
   if(::CopyTime(symbol,timeframe,index,1,array)!=1)
     {
      ::PrintFormat("%s: CopyTime for %s/%s, bar %ld failed. Error %lu",__FUNCTION__,symbol,::StringSubstr(::EnumToString(timeframe),7),index,::GetLastError());
      return LINE_STATE_NONE;
     }
//--- Get index of the first bar in indicator object buffer based on bar opening time on the chart
   bar0=::iBarShift(this.m_symbol,this.m_timeframe,array[0]);
   if(bar0==WRONG_VALUE)
     {
      ::PrintFormat("%s: iBarShift for %s/%s, time %s failed. Error %lu",__FUNCTION__,this.m_symbol,this.TimeframeDescription(),string(array[0]),::GetLastError());
      return LINE_STATE_NONE;
     }
//--- Get the time of the second bar on the chart
   ::ResetLastError();
   if(::CopyTime(symbol,timeframe,index+1,1,array)!=1)
     {
      ::PrintFormat("%s: CopyTime for %s/%s, bar %ld failed. Error %lu",__FUNCTION__,symbol,::StringSubstr(::EnumToString(timeframe),7),index+1,::GetLastError());
      return LINE_STATE_NONE;
     }
//--- Get index of the second bar in indicator object buffer based on bar opening time on the chart
   bar1=::iBarShift(this.m_symbol,this.m_timeframe,array[0]);
   if(bar1==WRONG_VALUE)
     {
      ::PrintFormat("%s: iBarShift for %s/%s, time %s failed. Error %lu",__FUNCTION__,this.m_symbol,this.TimeframeDescription(),string(array[0]),::GetLastError());
      return LINE_STATE_NONE;
     }
//--- Get the time of the third bar on the chart
   ::ResetLastError();
   if(::CopyTime(symbol,timeframe,index+2,1,array)!=1)
     {
      ::PrintFormat("%s: CopyTime for %s/%s, bar %ld failed. Error %lu",__FUNCTION__,symbol,::StringSubstr(::EnumToString(timeframe),7),index+2,::GetLastError());
      return LINE_STATE_NONE;
     }
//--- Get index of the third bar in indicator object buffer based on bar opening time on the chart
   bar2=::iBarShift(this.m_symbol,this.m_timeframe,array[0]);
   if(bar2==WRONG_VALUE)
     {
      ::PrintFormat("%s: iBarShift for %s/%s, time %s failed. Error %lu",__FUNCTION__,this.m_symbol,this.TimeframeDescription(),string(array[0]),::GetLastError());
      return LINE_STATE_NONE;
     }
     
//--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
   const double value0=this.GetData(buffer_num,bar0);
   const double value1=this.GetData(buffer_num,bar1);
   const double value2=this.GetData(buffer_num,bar2);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Line upward reversal (value2>value1 && value0>value1)
   if(::NormalizeDouble(value2-value1,this.m_digits)>0 && ::NormalizeDouble(value0-value1,this.m_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Line upward direction (value2<=value1 && value0>value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)<=0 && ::NormalizeDouble(value0-value1,this.m_digits)>0)
      return LINE_STATE_UP;
//--- Line upward stop (value2<=value1 && value0==value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)<=0 && ::NormalizeDouble(value0-value1,this.m_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Line downward reversal (value2<value1 && value0<value1)
   if(::NormalizeDouble(value2-value1,this.m_digits)<0 && ::NormalizeDouble(value0-value1,this.m_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Line downward direction (value2>=value1 && value0<value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)>=0 && ::NormalizeDouble(value0-value1,this.m_digits)<0)
      return LINE_STATE_DOWN;
//--- Line downward stop (value2>=value1 && value0==value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)>=0 && ::NormalizeDouble(value0-value1,this.m_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Undefined state
   return LINE_STATE_NONE;
  }

该方法用于获取指标对象线相对于当前图表的状态。如果指标对象是使用相对于当前图表的较高时间段的数据计算的,则其直线将在当前图表的条形图上“拉伸”。该方法允许您正确地获取当前图表指定柱上指标线的状态。

返回相对于指定水平的线状态的方法:

//+------------------------------------------------------------------+
//| Return the state of the line relative to the specified level     |
//+------------------------------------------------------------------+
ENUM_LINE_STATE CIndMSTF::BufferLineStateRelative(const int buffer_num,const int index,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Get the values of the indicator line with the shift (0,1) relative to the passed index
   const double value0=this.GetData(buffer_num,index);
   const double value1=this.GetData(buffer_num,index+1);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Define the second level to compare
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- The line is below the level (value1<level && value0<level0)
   if(::NormalizeDouble(value1-level,this.m_digits)<0 && ::NormalizeDouble(value0-level0,this.m_digits)<0)
      return LINE_STATE_BELOW;
//--- The line is above the level (value1>level && value0>level0)
   if(::NormalizeDouble(value1-level,this.m_digits)>0 && ::NormalizeDouble(value0-level0,this.m_digits)>0)
      return LINE_STATE_ABOVE;
//--- The line crossed the level upwards (value1<=level && value0>level0)
   if(::NormalizeDouble(value1-level,this.m_digits)<=0 && ::NormalizeDouble(value0-level0,this.m_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- The line crossed the level downwards (value1>=level && value0<level0)
   if(::NormalizeDouble(value1-level,this.m_digits)>=0 && ::NormalizeDouble(value0-level0,this.m_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- The line touched the level from below (value1<level0 && value0==level0)
   if(::NormalizeDouble(value1-level,this.m_digits)<0 && ::NormalizeDouble(value0-level0,this.m_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- The line touched the level from above (value1>level0 && value0==level0)
   if(::NormalizeDouble(value1-level,this.m_digits)>0 && ::NormalizeDouble(value0-level0,this.m_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Line is equal to the level value (value1==level0 && value0==level0)
   if(::NormalizeDouble(value1-level,this.m_digits)==0 && ::NormalizeDouble(value0-level0,this.m_digits)==0)
      return LINE_STATE_EQUALS;
//--- Undefined state
   return LINE_STATE_NONE;
  }

返回相对于指定图表交易品种/周期上指定水平的线状态的方法:

//+------------------------------------------------------------------+
//| Return the state of the line relative to the specified level     |
//| on the specified chart symbol/period                             |
//+------------------------------------------------------------------+
ENUM_LINE_STATE CIndMSTF::BufferLineStateRelative(const string symbol_from,const ENUM_TIMEFRAMES timeframes_from,const int buffer_num,const int index,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Determine the chart symbol/period passed to the method
   string symbol=(symbol_from=="" || symbol_from==NULL ? ::Symbol() : symbol_from);
   ENUM_TIMEFRAMES timeframe=(timeframes_from==PERIOD_CURRENT ? ::Period() : timeframes_from);
//--- Get the values of the indicator line with the shift (0,1) relative to the passed index
   const double value0=this.GetDataTo(symbol,timeframe,buffer_num,index);
   const double value1=this.GetDataTo(symbol,timeframe,buffer_num,index+1);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Define the second level to compare
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- The line is below the level (value1<level && value0<level0)
   if(::NormalizeDouble(value1-level,this.m_digits)<0 && ::NormalizeDouble(value0-level0,this.m_digits)<0)
      return LINE_STATE_BELOW;
//--- The line is above the level (value1>level && value0>level0)
   if(::NormalizeDouble(value1-level,this.m_digits)>0 && ::NormalizeDouble(value0-level0,this.m_digits)>0)
      return LINE_STATE_ABOVE;
//--- The line crossed the level upwards (value1<=level && value0>level0)
   if(::NormalizeDouble(value1-level,this.m_digits)<=0 && ::NormalizeDouble(value0-level0,this.m_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- The line crossed the level downwards (value1>=level && value0<level0)
   if(::NormalizeDouble(value1-level,this.m_digits)>=0 && ::NormalizeDouble(value0-level0,this.m_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- The line touched the level from below (value1<level0 && value0==level0)
   if(::NormalizeDouble(value1-level,this.m_digits)<0 && ::NormalizeDouble(value0-level0,this.m_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- The line touched the level from above (value1>level0 && value0==level0)
   if(::NormalizeDouble(value1-level,this.m_digits)>0 && ::NormalizeDouble(value0-level0,this.m_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Line is equal to the level value (value1==level0 && value0==level0)
   if(::NormalizeDouble(value1-level,this.m_digits)==0 && ::NormalizeDouble(value0-level0,this.m_digits)==0)
      return LINE_STATE_EQUALS;
//--- Undefined state
   return LINE_STATE_NONE;
  }

您可以在关于将振荡器连接到EA的文章中读到更多关于获取指标线状态的方法。

其它的类方法:

//+------------------------------------------------------------------+
//| Return category description                                      |
//+------------------------------------------------------------------+
string CIndMSTF::CategoryDescription(void)
  {
//--- Create a category name from ENUM_IND_CATEGORY and return the resulting text
   string category=::StringSubstr(::EnumToString(this.m_category),13);
   if(category.Lower())
      category.SetChar(0,ushort(category.GetChar(0)-0x20));
   return category;
  }
//+------------------------------------------------------------------+
//| Return the description of the indicator buffer                   |
//+------------------------------------------------------------------+
string CIndMSTF::BufferDescription(const uint buffer_num)
  {
//--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to the log and return empty string
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      return "";
     }
//--- If indicator has buffers, return description of the specified buffer, otherwise description of the indicator
   return(this.BuffersTotal()>0 ? this.m_buffers[buffer_num].descript : this.m_title);
  }
//+------------------------------------------------------------------+
//| Set indicator buffer description                                 |
//+------------------------------------------------------------------+
void CIndMSTF::SetBufferDescription(const uint buffer_num,const string descr)
  {
//--- If the indicator has no buffers, exit
   if(this.BuffersTotal()==0)
      return;
//--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to the log and exit
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      return;
     }
//--- Write the description passed to the method into the specified buffer
   this.m_buffers[buffer_num].descript=descr;
  }
//+------------------------------------------------------------------+
//| Disable timeseries indexing of buffer arrays                     |
//+------------------------------------------------------------------+
void CIndMSTF::SetAsSeriesOff(void)
  {
//--- In a loop through all indicator buffers, disable the array as timeseries flag
   for(int i=0;i<(int)this.BuffersTotal();i++)
      ::ArraySetAsSeries(this.m_buffers[i].array,false);
  }
//+------------------------------------------------------------------+
//| Returns the timeseries flag of the given buffer                  |
//+------------------------------------------------------------------+
bool CIndMSTF::IsSeries(const uint buffer_num) const
  {
//--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to the log and return 'false'
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      return false;
     }
//--- Return the timeseries flag of the array of the specified buffer
   return (bool)::ArrayGetAsSeries(this.m_buffers[buffer_num].array);
  }
//+------------------------------------------------------------------+
//| Returns the amount of data in the specified buffer               |
//+------------------------------------------------------------------+
uint CIndMSTF::DataTotal(const uint buffer_num) const
  {
//--- Validate the buffer number passed to method and, if number is incorrect, print a message to log and return zero
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      return 0;
     }
//--- Return the array size of the specified buffer
   return this.m_buffers[buffer_num].array.Size();
  }

多交易品种多周期指标对象的基类已准备就绪。该类包含使用不属于当前图表的时间序列数据构建的指标所需的所有功能。

要创建不同类型的技术指标,请从新创建的基本类创建派生类。在派生类的构造函数中,指示特定类型的指标所固有的参数和属性。


按类型划分的指标类

从基类派生的类将是最简单的,并且只包含一个构造函数。类构造函数将被传递用于计算指标的图表的交易品种/周期,以及特定于此类型指标的输入参数。在构造函数初始化行中,参数将传递给父类的构造函数。

一个类创建不包含任何参数的指标对象的示例:

//+------------------------------------------------------------------+
//| Accelerator Oscillator indicator class                           |
//+------------------------------------------------------------------+
class CIndAC : public CIndMSTF
  {
public:
//--- Constructor
   CIndAC(const string symbol,const ENUM_TIMEFRAMES timeframe) : CIndMSTF(IND_AC,1,symbol,timeframe)
     {
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=(current ? "" : StringFormat("(%s)",symbol_period));
      //--- Write description of parameters, indicator name, its description, title and category
      this.SetParameters(param);
      this.SetName("AC");
      this.SetDescription("Accelerator Oscillator");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_WILLIAMS;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };

应把其上计算指标的指标类型、缓冲区数量、图表交易品种和图表周期在初始化字符串中传递给父类。在类主体中创建一个包含指标参数描述的字符串。在这种情况下,如果在当前图表的数据上创建指标,则参数字符串将为空。否则,它将以“(EURUSD,H1)”的形式包含图表的交易品种和周期。接下来,在构造函数的主体中设置这种类型的指标(这里是加速振荡指标)所固有的所有参数。

对于每个指标,可以设置数据窗口和交易品种图表中显示的小数位数。该类构造函数没有“Digits”设置,因为该值等于计算指标的交易品种的小数位数,是在父类的构造函数中设置的。如果有必要为指标设置不同的小数位数,则应在数字与交易品种不同的指标类的构造函数中完成,或者可以在使用SetDigits()方法创建指标对象后更改。

具有参数的指标类:

//+------------------------------------------------------------------+
//| Accumulation/Distribution indicator class                        |
//+------------------------------------------------------------------+
class CIndAD : public CIndMSTF
  {
public:
//--- Constructor
   CIndAD(const string symbol,const ENUM_TIMEFRAMES timeframe,
          const ENUM_APPLIED_VOLUME applied_volume // used volume
         ) : CIndMSTF(IND_AD,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=applied_volume;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=(current ? "" : StringFormat("(%s)",symbol_period));
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("A/D");
      this.SetDescription("Accumulation/Distribution");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_VOLUME;
      this.m_digits=0;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };

这里我们有一些参数,它们应该注册在指标输入参数MqlParam的数组结构中。在这里,我们还设置了指标的Digits值,该值是为标准累积/分布指标设置的。

从多交易品种多周期指标的基类派生的所有类的完整列表:

//+------------------------------------------------------------------+
//| Accelerator Oscillator indicator class                           |
//+------------------------------------------------------------------+
class CIndAC : public CIndMSTF
  {
public:
//--- Constructor
   CIndAC(const string symbol,const ENUM_TIMEFRAMES timeframe) : CIndMSTF(IND_AC,1,symbol,timeframe)
     {
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=(current ? "" : StringFormat("(%s)",symbol_period));
      //--- Write description of parameters, indicator name, its description, title and category
      this.SetParameters(param);
      this.SetName("AC");
      this.SetDescription("Accelerator Oscillator");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_WILLIAMS;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Accumulation/Distribution indicator class                        |
//+------------------------------------------------------------------+
class CIndAD : public CIndMSTF
  {
public:
//--- Constructor
   CIndAD(const string symbol,const ENUM_TIMEFRAMES timeframe,
          const ENUM_APPLIED_VOLUME applied_volume // used volume
         ) : CIndMSTF(IND_AD,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=applied_volume;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=(current ? "" : StringFormat("(%s)",symbol_period));
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("A/D");
      this.SetDescription("Accumulation/Distribution");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_VOLUME;
      this.m_digits=0;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Average Directional Movement Index indicator class               |
//+------------------------------------------------------------------+
class CIndADX : public CIndMSTF
  {
public:
//--- Constructor
   CIndADX(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int adx_period                    // averaging period
          ) : CIndMSTF(IND_ADX,3,symbol,timeframe)
     {
      // Номера буферов: 0 - MAIN_LINE, 1 - PLUSDI_LINE, 2 - MINUSDI_LINE
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(adx_period<1 ? 14 : adx_period);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),adx_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("ADX");
      this.SetDescription("Average Directional Movement Index");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=2;
      //--- write descriptions of MAIN_LINE, PLUSDI_LINE and MINUSDI_LINE line buffers
      this.SetBufferDescription(MAIN_LINE,this.m_title);
      this.SetBufferDescription(PLUSDI_LINE,"+DI");
      this.SetBufferDescription(MINUSDI_LINE,"-DI");
     }
  };
//+------------------------------------------------------------------+
//| Average Directional Movement Index Wilder indicator class        |
//+------------------------------------------------------------------+
class CIndADXW : public CIndMSTF
  {
public:
//--- Constructor
   CIndADXW(const string symbol,const ENUM_TIMEFRAMES timeframe,
            const int adx_period                      // averaging period
           ) : CIndMSTF(IND_ADXW,3,symbol,timeframe)
     {
      // Номера буферов: 0 - MAIN_LINE, 1 - PLUSDI_LINE, 2 - MINUSDI_LINE
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(adx_period<1 ? 14 : adx_period);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),adx_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("ADX Wilder");
      this.SetDescription("Average Directional Movement Index Wilder");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=2;
      //--- write descriptions of MAIN_LINE, PLUSDI_LINE and MINUSDI_LINE line buffers
      this.SetBufferDescription(MAIN_LINE,this.m_title);
      this.SetBufferDescription(PLUSDI_LINE,"+DI");
      this.SetBufferDescription(MINUSDI_LINE,"-DI");
     }
  };
//+------------------------------------------------------------------+
//| Alligator indicator class                                        |
//+------------------------------------------------------------------+
class CIndAlligator : public CIndMSTF
  {
public:
//--- Constructor
   CIndAlligator(const string symbol,const ENUM_TIMEFRAMES timeframe,
                 const int jaw_period,                   // period for calculating jaws
                 const int jaw_shift,                    // horizontal shift of jaws
                 const int teeth_period,                 // period for calculating teeth
                 const int teeth_shift,                  // horizontal shift of teeth
                 const int lips_period,                  // period for calculating lips
                 const int lips_shift,                   // horizontal shift of lips
                 const ENUM_MA_METHOD ma_method,         // smoothing type
                 const ENUM_APPLIED_PRICE applied_price  // price type or handle
                ) : CIndMSTF(IND_ALLIGATOR,3,symbol,timeframe)
     {
      // Buffer indexes: 0 - GATORJAW_LINE, 1 - GATORTEETH_LINE, 2 - GATORLIPS_LINE
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,8)==8)
        {
         ::ZeroMemory(this.m_param);
         //--- period for jaw line calculation
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(jaw_period<1 ? 13 : jaw_period);
         //--- horizontal shift of the jaw line
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=jaw_shift;
         //--- period for teeth line calculation
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(teeth_period<1 ? 8 : teeth_period);
         //--- horizontal shift of teeth line
         this.m_param[3].type=TYPE_INT;
         this.m_param[3].integer_value=teeth_shift;
         //--- period for lip line calculation
         this.m_param[4].type=TYPE_UINT;
         this.m_param[4].integer_value=(lips_period<1 ? 5 : lips_period);
         //--- horizontal shift of lips line
         this.m_param[5].type=TYPE_INT;
         this.m_param[5].integer_value=lips_shift;
         //--- smoothing type
         this.m_param[6].type=TYPE_UINT;
         this.m_param[6].integer_value=ma_method;
         //--- price type or handle
         this.m_param[7].type=TYPE_UINT;
         this.m_param[7].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),jaw_period,teeth_period,lips_period);
      //--- Write description of parameters, indicator name, its description, title and category
      this.SetParameters(param);
      this.SetName("Alligator");
      this.SetDescription("Alligator");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_WILLIAMS;
      //--- Write descriptions of GATORJAW_LINE, GATORTEETH_LINE and GATORLIPS_LINE line buffers
      this.SetBufferDescription(GATORJAW_LINE,::StringFormat("Jaws(%s%lu)", (current ? "" : symbol_period+":"),jaw_period));
      this.SetBufferDescription(GATORTEETH_LINE,::StringFormat("Teeth(%s%lu)",(current ? "" : symbol_period+":"),teeth_period));
      this.SetBufferDescription(GATORLIPS_LINE,::StringFormat("Lips(%s%lu)", (current ? "" : symbol_period+":"),lips_period));
      //--- Write offsets to buffers GATORJAW_LINE, GATORTEETH_LINE and GATORLIPS_LINE
      this.SetBufferShift(GATORJAW_LINE,jaw_shift);
      this.SetBufferShift(GATORTEETH_LINE,teeth_shift);
      this.SetBufferShift(GATORLIPS_LINE,lips_shift);
     }
  };
//+------------------------------------------------------------------+
//| Adaptive Moving Average indicator class                          |
//+------------------------------------------------------------------+
class CIndAMA : public CIndMSTF
  {
public:
//--- Constructor
   CIndAMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int ama_period,                   // AMA period
           const int fast_ma_period,               // fast MA period
           const int slow_ma_period,               // slow MA period
           const int ama_shift,                    // horizontal shift of the indicator
           const ENUM_APPLIED_PRICE applied_price  // price type or handle
          ) : CIndMSTF(IND_AMA,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,5)==5)
        {
         ::ZeroMemory(this.m_param);
         //--- AMA period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ama_period<1 ? 9 : ama_period);
         //--- fast MA period
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(fast_ma_period<1 ? 2 : fast_ma_period);
         //--- slow MA period
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(slow_ma_period<1 ? 30 : slow_ma_period);
         //--- horizontal shift of the indicator
         this.m_param[3].type=TYPE_INT;
         this.m_param[3].integer_value=ama_shift;
         //--- price type or handle
         this.m_param[4].type=TYPE_UINT;
         this.m_param[4].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),ama_period,fast_ma_period,slow_ma_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("AMA");
      this.SetDescription("Adaptive Moving Average");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
      //--- Write shift to buffer 0
      this.SetBufferShift(0,ama_shift);
     }
  };
//+------------------------------------------------------------------+
//| Awesome Oscillator indicator class                               |
//+------------------------------------------------------------------+
class CIndAO : public CIndMSTF
  {
public:
//--- Constructor
   CIndAO(const string symbol,const ENUM_TIMEFRAMES timeframe) : CIndMSTF(IND_AO,1,symbol,timeframe)
     {
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=(current ? "" : StringFormat("(%s)",symbol_period));
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("AO");
      this.SetDescription("Awesome Oscillator");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_WILLIAMS;
      this.m_digits=::Digits()+1;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Average True Range indicator class                               |
//+------------------------------------------------------------------+
class CIndATR : public CIndMSTF
  {
public:
//--- Constructor
   CIndATR(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int ma_period                     // averaging period
          ) : CIndMSTF(IND_ATR,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Write description of parameters, indicator name, its description, title and category
      this.SetParameters(param);
      this.SetName("ATR");
      this.SetDescription("Average True Range");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Bears Power indicator class                                      |
//+------------------------------------------------------------------+
class CIndBears : public CIndMSTF
  {
public:
//--- Constructor
   CIndBears(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int ma_period                      // averaging period
            ) : CIndMSTF(IND_BEARS,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 13 : ma_period);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("Bears");
      this.SetDescription("Bears Power");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=::Digits()+1;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Bulls Power indicator class                                      |
//+------------------------------------------------------------------+
class CIndBulls : public CIndMSTF
  {
public:
//--- Constructor
   CIndBulls(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int ma_period                      // averaging period
            ) : CIndMSTF(IND_BULLS,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 13 : ma_period);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("Bulls");
      this.SetDescription("Bulls Power");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=::Digits()+1;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Bollinger Bands® indicator class                                 |
//+------------------------------------------------------------------+
class CIndBands : public CIndMSTF
  {
public:
//--- Constructor
   CIndBands(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int bands_period,                  // central line calculation period
             const int bands_shift,                   // horizontal shift of the indicator
             const double deviation,                  // number of standard deviations
             const ENUM_APPLIED_PRICE applied_price   // price type or handle
            ) : CIndMSTF(IND_BANDS,3,symbol,timeframe)
     {
      // Buffer indexes: 0 - BASE_LINE, 1 - UPPER_BAND, 2 - LOWER_BAND
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- central line calculation period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(bands_period<1 ? 20 : bands_period);
         //--- horizontal shift of the indicator
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=bands_shift;
         //--- number of standard deviations
         this.m_param[2].type=TYPE_DOUBLE;
         this.m_param[2].double_value=deviation;
         //--- price type or handle
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),bands_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("Bands");
      this.SetDescription("Bollinger Bands");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Description of line buffers BASE_LINE, UPPER_BAND and LOWER_BAND
      this.SetBufferDescription(BASE_LINE,this.m_title+" Middle");
      this.SetBufferDescription(UPPER_BAND,this.m_title+" Upper");
      this.SetBufferDescription(LOWER_BAND,this.m_title+" Lower");
      //--- Write offsets to the BASE_LINE, UPPER_BAND and LOWER_BAND buffers
      this.SetBufferShift(BASE_LINE,bands_shift);
      this.SetBufferShift(UPPER_BAND,bands_shift);
      this.SetBufferShift(LOWER_BAND,bands_shift);
     }
  };
//+------------------------------------------------------------------+
//| Commodity Channel Index indicator class                          |
//+------------------------------------------------------------------+
class CIndCCI : public CIndMSTF
  {
public:
//--- Constructor
   CIndCCI(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int ma_period,                    // averaging period
           const ENUM_APPLIED_PRICE applied_price  // price type or handle
          ) : CIndMSTF(IND_CCI,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,2)==2)
        {
         ::ZeroMemory(this.m_param);
         //--- averaging period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period<2 ? 2 : ma_period);
         //--- price type or handle
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("CCI");
      this.SetDescription("Commodity Channel Index");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=2;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Chaikin Oscillator indicator class                               |
//+------------------------------------------------------------------+
class CIndCHO : public CIndMSTF
  {
public:
//--- Constructor
   CIndCHO(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int fast_ma_period,                  // fast period
           const int slow_ma_period,                  // slow period
           const ENUM_MA_METHOD ma_method,            // smoothing type
           const ENUM_APPLIED_VOLUME applied_volume   // used volume
          ) : CIndMSTF(IND_CHAIKIN,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- fast period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(fast_ma_period<1 ? 3 : fast_ma_period);
         //--- slow period
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(slow_ma_period<1 ? 10 : slow_ma_period);
         //--- smoothing type
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=ma_method;
         //--- used volume
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=applied_volume;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu,%lu)",symbol_period,(current ? "" : ":"),slow_ma_period,fast_ma_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("CHO");
      this.SetDescription("Chaikin Oscillator");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=0;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Double Exponential Moving Average indicator class                |
//+------------------------------------------------------------------+
class CIndDEMA : public CIndMSTF
  {
public:
//--- Constructor
   CIndDEMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
            const int ma_period,                      // averaging period
            const int ma_shift,                       // horizontal indicator shift
            const ENUM_APPLIED_PRICE applied_price    // price type or handle
          ) : CIndMSTF(IND_DEMA,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,3)==3)
        {
         ::ZeroMemory(this.m_param);
         //--- averaging period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
         //--- horizontal shift of the indicator
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=ma_shift;
         //--- price type or handle
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("DEMA");
      this.SetDescription("Double Exponential Moving Average");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
      //--- Write shift to buffer 0
      this.SetBufferShift(0,ma_shift);
     }
  };
//+------------------------------------------------------------------+
//| DeMarker indicator class                                         |
//+------------------------------------------------------------------+
class CIndDeM : public CIndMSTF
  {
public:
//--- Constructor
   CIndDeM(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int ma_period                     // averaging period
          ) : CIndMSTF(IND_DEMARKER,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         //--- averaging period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("DeM");
      this.SetDescription("DeMarker");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=3;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Envelopes indicator class                                        |
//+------------------------------------------------------------------+
class CIndEnvelopes : public CIndMSTF
  {
public:
//--- Constructor
   CIndEnvelopes(const string symbol,const ENUM_TIMEFRAMES timeframe,
                 const int ma_period,                    // middle line calculation period
                 const int ma_shift,                     // horizontal shift of the indicator
                 const ENUM_MA_METHOD ma_method,         // smoothing type
                 const ENUM_APPLIED_PRICE applied_price, // price type or handle
                 const double deviation                  // deviation of envelope borders from the middle line
          ) : CIndMSTF(IND_ENVELOPES,2,symbol,timeframe)
     {
      // Buffer indexes: 0 - UPPER_LINE, 1 - LOWER_LINE
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,5)==5)
        {
         ::ZeroMemory(this.m_param);
         //--- central line calculation period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
         //--- horizontal shift of the indicator
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=ma_shift;
         //--- smoothing type
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=ma_method;
         //--- price type or handle
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=applied_price;
         //--- deviation of envelope borders from the muddle line
         this.m_param[4].type=TYPE_UINT;
         this.m_param[4].double_value=deviation;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("Envelopes");
      this.SetDescription("Envelopes");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Description of UPPER_LINE and LOWER_LINE line buffers
      this.SetBufferDescription(UPPER_LINE,this.m_title+" Upper");
      this.SetBufferDescription(LOWER_LINE,this.m_title+" Lower");
      //--- Write shift to buffer 0
      this.SetBufferShift(0,ma_shift);
     }
  };
//+------------------------------------------------------------------+
//| Force Index indicator class                                      |
//+------------------------------------------------------------------+
class CIndForce : public CIndMSTF
  {
public:
//--- Constructor
   CIndForce(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int                 ma_period,     // averaging period
             const ENUM_MA_METHOD      ma_method,     // smoothing type
             const ENUM_APPLIED_VOLUME applied_volume // volume type for calculation
            ) : CIndMSTF(IND_FORCE,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,3)==3)
        {
         ::ZeroMemory(this.m_param);
         //--- averaging period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 13 : ma_period);
         //--- smoothing type
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=ma_method;
         //--- volume type for calculation
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=applied_volume;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("Force");
      this.SetDescription("Force Index");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=::Digits()+1;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Fractals indicator class                                         |
//+------------------------------------------------------------------+
class CIndFractals : public CIndMSTF
  {
public:
//--- Constructor
   CIndFractals(const string symbol,const ENUM_TIMEFRAMES timeframe) : CIndMSTF(IND_FRACTALS,2,symbol,timeframe)
     {
      // Buffer indexes: 0 - UPPER_LINE, 1 - LOWER_LINE
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=(current ? "" : StringFormat("(%s)",symbol_period));
      //--- Write description of parameters, indicator name, its description, title and category
      this.SetParameters(param);
      this.SetName("Fractals");
      this.SetDescription("Fractals");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_WILLIAMS;
      //--- Description of UPPER_LINE and LOWER_LINE line buffers
      this.SetBufferDescription(UPPER_LINE,this.m_title+" Up");
      this.SetBufferDescription(LOWER_LINE,this.m_title+" Down");
     }
  };
//+------------------------------------------------------------------+
//| Fractal Adaptive Moving Average indicator class                  |
//+------------------------------------------------------------------+
class CIndFrAMA : public CIndMSTF
  {
public:
//--- Constructor
   CIndFrAMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int ma_period,                     // averaging period
             const int ma_shift,                      // horizontal shift of the indicator
             const ENUM_APPLIED_PRICE applied_price   // price type or handle
            ) : CIndMSTF(IND_FRAMA,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,3)==3)
        {
         ::ZeroMemory(this.m_param);
         //--- averaging period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
         //--- horizontal shift of the indicator
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=ma_shift;
         //--- price type or handle
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("FRAMA");
      this.SetDescription("Fractal Adaptive Moving Average");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
      //--- Write shift to buffer 0
      this.SetBufferShift(0,ma_shift);
     }
  };
//+------------------------------------------------------------------+
//| Gator Oscillator indicator class                                 |
//+------------------------------------------------------------------+
class CIndGator : public CIndMSTF
  {
public:
//--- Constructor
   CIndGator(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int jaw_period,                    // period for jaw line calculation
             const int jaw_shift,                     // horizontal shift of jaw line
             const int teeth_period,                  // period for calculating teeth line
             const int teeth_shift,                   // horizontal shift of teeth line
             const int lips_period,                   // period for calculating lip line
             const int lips_shift,                    // horizontal shift of lip line
             const ENUM_MA_METHOD ma_method,          // smoothing type
             const ENUM_APPLIED_PRICE applied_price   // price type or handle
            ) : CIndMSTF(IND_GATOR,4,symbol,timeframe)
     {
      // Buffer indexes: 0 - UPPER_HISTOGRAM, 1 - color buffer of the upper histogram, 2 - LOWER_HISTOGRAM, 3 - color buffer of the lower histogram
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,8)==8)
        {
         ::ZeroMemory(this.m_param);
         //--- period for jaw line calculation
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(jaw_period<1 ? 13 : jaw_period);
         //--- horizontal shift of the jaw line
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=jaw_shift;
         //--- period for teeth line calculation
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(teeth_period<1 ? 8 : teeth_period);
         //--- horizontal shift of teeth line
         this.m_param[3].type=TYPE_INT;
         this.m_param[3].integer_value=teeth_shift;
         //--- period for lip line calculation
         this.m_param[4].type=TYPE_UINT;
         this.m_param[4].integer_value=(lips_period<1 ? 5 : lips_period);
         //--- horizontal shift of lips line
         this.m_param[5].type=TYPE_INT;
         this.m_param[5].integer_value=lips_shift;
         //--- smoothing type
         this.m_param[6].type=TYPE_UINT;
         this.m_param[6].integer_value=ma_method;
         //--- price type or handle
         this.m_param[7].type=TYPE_UINT;
         this.m_param[7].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),jaw_period,teeth_period,lips_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("Gator");
      this.SetDescription("Gator Oscillator");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_WILLIAMS;
      this.m_digits=::Digits()+1;
      //--- Description of line buffers UPPER_HISTOGRAM, upper histogram color buffer, LOWER_HISTOGRAM and lower histogram color buffer
      this.SetBufferDescription(UPPER_HISTOGRAM,this.m_title+" Up");
      this.SetBufferDescription(1,this.m_title+" Colors Up");
      this.SetBufferDescription(LOWER_HISTOGRAM,this.m_title+" Down");
      this.SetBufferDescription(3,this.m_title+" Colors Down");
      //--- Записываем смещения в буферы UPPER_HISTOGRAM, 1, LOWER_HISTOGRAM и 2
      this.SetBufferShift(UPPER_HISTOGRAM,teeth_shift);
      this.SetBufferShift(1,teeth_shift);
      this.SetBufferShift(LOWER_HISTOGRAM,lips_shift);
      this.SetBufferShift(3,lips_shift);
     }
  };
//+------------------------------------------------------------------+
//| Ichimoku Kinko Hyo indicator class                               |
//+------------------------------------------------------------------+
class CIndIchimoku : public CIndMSTF
  {
public:
//--- Constructor
   CIndIchimoku(const string symbol,const ENUM_TIMEFRAMES timeframe,
                const int tenkan_sen,                    // period of Tenkan-sen
                const int kijun_sen,                     // period of Kijun-sen
                const int senkou_span_b                  // period of Senkou Span B
               ) : CIndMSTF(IND_ICHIMOKU,5,symbol,timeframe)
     {
      // Buffer indexes: 0 - TENKANSEN_LINE, 1 - KIJUNSEN_LINE, 2 - SENKOUSPANA_LINE, 3 - SENKOUSPANB_LINE, 4 - CHIKOUSPAN_LINE
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,3)==3)
        {
         ::ZeroMemory(this.m_param);
         //--- period of Tenkan-sen
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(tenkan_sen<1 ? 9 : tenkan_sen);
         //--- period of Kijun-sen
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(kijun_sen<1 ? 26 : kijun_sen);
         //--- period of Senkou Span B
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(senkou_span_b<1 ? 52 : senkou_span_b);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),tenkan_sen,kijun_sen,senkou_span_b);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("Ichimoku");
      this.SetDescription("Ichimoku Kinko Hyo");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Description of line buffers TENKANSEN_LINE, KIJUNSEN_LINE, SENKOUSPANA_LINE, SENKOUSPANB_LINE and CHIKOUSPAN_LINE
      this.SetBufferDescription(TENKANSEN_LINE,::StringFormat("Tenkan-sen(%lu)",tenkan_sen));
      this.SetBufferDescription(KIJUNSEN_LINE,::StringFormat("Kijun-sen(%lu)",kijun_sen));
      this.SetBufferDescription(SENKOUSPANA_LINE,"Senkou Span A");
      this.SetBufferDescription(SENKOUSPANB_LINE,::StringFormat("Senkou Span B(%lu)",senkou_span_b));
      this.SetBufferDescription(CHIKOUSPAN_LINE,"Chikou Span");
      //--- Write shifts to buffers SENKOUSPANA_LINE, SENKOUSPANB_LINE and CHIKOUSPAN_LINE
      //this.SetBufferShift(SENKOUSPANA_LINE,kijun_sen);
      //this.SetBufferShift(SENKOUSPANB_LINE,kijun_sen);
      //this.SetBufferShift(CHIKOUSPAN_LINE,kijun_sen-senkou_span_b);
     }
  };
//+------------------------------------------------------------------+
//| Market Facilitation Index indicator class                        |
//+------------------------------------------------------------------+
class CIndBWMFI : public CIndMSTF
  {
public:
//--- Constructor
   CIndBWMFI(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const ENUM_APPLIED_VOLUME applied_volume // volume type for calculation
            ) : CIndMSTF(IND_BWMFI,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         //--- volume type for calculation
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=applied_volume;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=(current ? "" : StringFormat("(%s)",symbol_period));
      //--- Write description of parameters, indicator name, its description, title and category
      this.SetParameters(param);
      this.SetName("BW MFI");
      this.SetDescription("Market Facilitation Index");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_WILLIAMS;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Momentum indicator class                                         |
//+------------------------------------------------------------------+
class CIndMomentum : public CIndMSTF
  {
public:
//--- Constructor
   CIndMomentum(const string symbol,const ENUM_TIMEFRAMES timeframe,
                const int                 mom_period,    // averaging period
                const ENUM_APPLIED_PRICE  applied_price  // price type or handle
               ) : CIndMSTF(IND_MOMENTUM,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,2)==2)
        {
         ::ZeroMemory(this.m_param);
         //--- averaging period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(mom_period<1 ? 14 : mom_period);
         //--- price type or handle
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),mom_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("Momentum");
      this.SetDescription("Momentum");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=2;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Money Flow Index indicator class                                 |
//+------------------------------------------------------------------+
class CIndMFI : public CIndMSTF
  {
public:
//--- Constructor
   CIndMFI(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int                 ma_period,       // averaging period
           const ENUM_APPLIED_VOLUME applied_volume   // volume type for calculation
          ) : CIndMSTF(IND_MFI,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,2)==2)
        {
         ::ZeroMemory(this.m_param);
         //--- averaging period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
         //--- volume type for calculation
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=applied_volume;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Write description of parameters, indicator name, its description, title and category
      this.SetParameters(param);
      this.SetName("MFI");
      this.SetDescription("Money Flow Index");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_VOLUME;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Moving Average indicator class                                   |
//+------------------------------------------------------------------+
class CIndMA : public CIndMSTF
  {
public:
//--- Constructor
   CIndMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
          const int                 ma_period,     // averaging period
          const int                 ma_shift,      // horizontal shift of the indicator
          const ENUM_MA_METHOD      ma_method,     // smoothing type
          const ENUM_APPLIED_PRICE  applied_price  // price type or handle
         ) : CIndMSTF(IND_MA,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- averaging period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 10 : ma_period);
         //--- horizontal shift of the indicator
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=ma_shift;
         //--- smoothing type
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=ma_method;
         //--- price type or handle
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("MA");
      this.SetDescription("Moving Average");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
      //--- Write shift to buffer 0
      this.SetBufferShift(0,ma_shift);
     }
  };
//+------------------------------------------------------------------+
//| Moving Average of Oscillator indicator class                     |
//+------------------------------------------------------------------+
class CIndOsMA : public CIndMSTF
  {
public:
//--- Constructor
   CIndOsMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
            const int                fast_ema_period, // fast MA period
            const int                slow_ema_period, // slow MA period
            const int                signal_period,   // difference averaging period
            const ENUM_APPLIED_PRICE applied_price    // price type or handle
           ) : CIndMSTF(IND_OSMA,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- fast MA period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(fast_ema_period<1 ? 12 : fast_ema_period);
         //--- slow MA period
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(slow_ema_period<1 ? 26 : slow_ema_period);
         //--- difference averaging period
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(signal_period<1 ? 9 : signal_period<2 ? 2 : signal_period);
         //--- price type or handle
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),fast_ema_period,slow_ema_period,signal_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("OsMA");
      this.SetDescription("Moving Average of Oscillator");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=::Digits()+2;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Moving Averages Convergence/Divergence indicator class           |
//+------------------------------------------------------------------+
class CIndMACD : public CIndMSTF
  {
public:
//--- Constructor
   CIndMACD(const string symbol,const ENUM_TIMEFRAMES timeframe,
            const int                fast_ema_period, // fast MA period
            const int                slow_ema_period, // slow MA period
            const int                signal_period,   // difference averaging period
            const ENUM_APPLIED_PRICE applied_price    // price type or handle
           ) : CIndMSTF(IND_MACD,2,symbol,timeframe)
     {
      // Buffer indexes: 0 - MAIN_LINE, 1 - SIGNAL_LINE
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- fast MA period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(fast_ema_period<1 ? 12 : fast_ema_period);
         //--- slow MA period
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(slow_ema_period<1 ? 26 : slow_ema_period);
         //--- difference averaging period
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(signal_period<1 ? 9 : signal_period);
         //--- price type or handle
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),fast_ema_period,slow_ema_period,signal_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("MACD");
      this.SetDescription("Moving Averages Convergence/Divergence");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=::Digits()+1;
      //--- Description of MAIN_LINE and SIGNAL_LINE line buffers
      this.SetBufferDescription(MAIN_LINE,this.m_title);
      this.SetBufferDescription(SIGNAL_LINE,"Signal");
     }
  };
//+------------------------------------------------------------------+
//| On Balance Volume indicator class                                |
//+------------------------------------------------------------------+
class CIndOBV : public CIndMSTF
  {
public:
//--- Constructor
   CIndOBV(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const ENUM_APPLIED_VOLUME applied_volume   // volume type for calculation
          ) : CIndMSTF(IND_OBV,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         //--- volume type for calculation
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=applied_volume;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=(current ? "" : StringFormat("(%s)",symbol_period));
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("OBV");
      this.SetDescription("On Balance Volume");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_VOLUME;
      this.m_digits=0;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Parabolic Stop and Reverse system indicator class                |
//+------------------------------------------------------------------+
class CIndSAR : public CIndMSTF
  {
public:
//--- Constructor
   CIndSAR(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const double step,                      // price change step — acceleration factor
           const double maximum                    // maximum step
          ) : CIndMSTF(IND_SAR,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,2)==2)
        {
         ::ZeroMemory(this.m_param);
         //--- price change step — acceleration factor
         this.m_param[0].type=TYPE_DOUBLE;
         this.m_param[0].double_value=step;
         //--- maximum step
         this.m_param[1].type=TYPE_DOUBLE;
         this.m_param[1].double_value=maximum;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%.2f,%.2f)",symbol_period,(current ? "" : ":"),step,maximum);
      //--- Write description of parameters, indicator name, its description, title and category
      this.SetParameters(param);
      this.SetName("SAR");
      this.SetDescription("Parabolic SAR");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Relative Strength Index                         |
//+------------------------------------------------------------------+
class CIndRSI : public CIndMSTF
  {
public:
//--- Constructor
   CIndRSI(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int                ma_period,     // averaging period
           const ENUM_APPLIED_PRICE applied_price  // price type or handle
          ) : CIndMSTF(IND_RSI,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,2)==2)
        {
         ::ZeroMemory(this.m_param);
         //--- averaging period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period<2 ? 2 : ma_period);
         //--- price type or handle
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("RSI");
      this.SetDescription("Relative Strength Index");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=2;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Relative Vigor Index indicator class                             |
//+------------------------------------------------------------------+
class CIndRVI : public CIndMSTF
  {
public:
//--- Constructor
   CIndRVI(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int ma_period                     // averaging period
          ) : CIndMSTF(IND_RVI,2,symbol,timeframe)
     {
      // Buffer indexes: 0 - MAIN_LINE, 1 - SIGNAL_LINE
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         //--- averaging period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 10 : ma_period);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("RVI");
      this.SetDescription("Relative Vigor Index");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=3;
      //--- Description of MAIN_LINE and SIGNAL_LINE line buffers
      this.SetBufferDescription(MAIN_LINE,this.m_title);
      this.SetBufferDescription(SIGNAL_LINE,"Signal");
     }
  };
//+------------------------------------------------------------------+
//| Standard Deviation indicator class                               |
//+------------------------------------------------------------------+
class CIndStdDev : public CIndMSTF
  {
public:
//--- Constructor
   CIndStdDev(const string symbol,const ENUM_TIMEFRAMES timeframe,
              const int                ma_period,        // averaging period
              const int                ma_shift,         // horizontal shift of the indicator
              const ENUM_MA_METHOD     ma_method,        // smoothing type
              const ENUM_APPLIED_PRICE applied_price     // price type or handle
             ) : CIndMSTF(IND_STDDEV,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- averaging period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 20 : ma_period<2 ? 2 : ma_period);
         //--- horizontal shift of the indicator
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=ma_shift;
         //--- smoothing type
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=ma_method;
         //--- price type or handle
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("StdDev");
      this.SetDescription("Standard Deviation");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
      //--- Write shift to buffer 0
      this.SetBufferShift(0,ma_shift);
     }
  };
//+------------------------------------------------------------------+
//| Stochastic Oscillator indicator class                            |
//+------------------------------------------------------------------+
class CIndStoch : public CIndMSTF
  {
public:
//--- Constructor
   CIndStoch(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int              Kperiod,          // K-period (number of bars for calculations)
             const int              Dperiod,          // D-period (primary smoothing period)
             const int              slowing,          // final smoothing
             const ENUM_MA_METHOD   ma_method,        // smoothing type
             const ENUM_STO_PRICE   price_field       // Stochastic calculation method
            ) : CIndMSTF(IND_STOCHASTIC,2,symbol,timeframe)
     {
      // Buffer indexes: 0 - MAIN_LINE, 1 - SIGNAL_LINE
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,5)==5)
        {
         ::ZeroMemory(this.m_param);
         //--- K period (number of bars for calculation)
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(Kperiod<1 ? 5 : Kperiod);
         //--- D period (primary smoothing period)
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(Dperiod<1 ? 3 : Dperiod);
         //--- final smoothing
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(slowing<1 ? 3 : slowing);
         //--- smoothing type
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=ma_method;
         //--- Stochastic calculation method
         this.m_param[4].type=TYPE_UINT;
         this.m_param[4].integer_value=price_field;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),Kperiod,Dperiod,slowing);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("Stoch");
      this.SetDescription("Stochastic Oscillator");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=2;
      //--- Description of MAIN_LINE and SIGNAL_LINE line buffers
      this.SetBufferDescription(MAIN_LINE,this.m_title);
      this.SetBufferDescription(SIGNAL_LINE,"Signal");
     }
  };
//+------------------------------------------------------------------+
//| Triple Exponential Moving Average indicator class                |
//+------------------------------------------------------------------+
class CIndTEMA : public CIndMSTF
  {
public:
//--- Constructor
   CIndTEMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
            const int                  ma_period,     // averaging period
            const int                  ma_shift,      // horizontal shift of the indicator
            const ENUM_APPLIED_PRICE   applied_price  // price type or handle
           ) : CIndMSTF(IND_TEMA,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,3)==3)
        {
         ::ZeroMemory(this.m_param);
         //--- averaging period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
         //--- horizontal shift of the indicator
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=ma_shift;
         //--- price type or handle
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("TEMA");
      this.SetDescription("Triple Exponential Moving Average");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
      //--- Write shift to buffer 0
      this.SetBufferShift(0,ma_shift);
     }
  };
//+------------------------------------------------------------------+
//| Triple Exponential Moving Averages Oscillator indicator class    |
//+------------------------------------------------------------------+
class CIndTriX : public CIndMSTF
  {
public:
//--- Constructor
   CIndTriX(const string symbol,const ENUM_TIMEFRAMES timeframe,
            const int                ma_period,       // averaging period
            const ENUM_APPLIED_PRICE applied_price    // price type or handle
           ) : CIndMSTF(IND_TRIX,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,2)==2)
        {
         ::ZeroMemory(this.m_param);
         //--- averaging period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period<2 ? 2 : ma_period);
         //--- price type or handle
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Write description of parameters, indicator name, its description, title and category
      this.SetParameters(param);
      this.SetName("TRIX");
      this.SetDescription("Triple Exponential Average");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Larry Williams' Percent Range indicator class                    |
//+------------------------------------------------------------------+
class CIndWPR : public CIndMSTF
  {
public:
//--- Constructor
   CIndWPR(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int calc_period                   // averaging period
          ) : CIndMSTF(IND_WPR,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         //--- averaging period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(calc_period<1 ? 14 : calc_period);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),calc_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("%R");
      this.SetDescription("Williams' Percent Range");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=2;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Variable Index Dynamic Average indicator class                   |
//+------------------------------------------------------------------+
class CIndVIDyA : public CIndMSTF
  {
public:
//--- Constructor
   CIndVIDyA(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int                 cmo_period,    // the Chande Momentum period
             const int                 ema_period,    // period of the smoothing factor
             const int                 ma_shift,      // horizontal shift of the indicator
             const ENUM_APPLIED_PRICE  applied_price  // price type or handle
            ) : CIndMSTF(IND_VIDYA,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- Chande Momentum period
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(cmo_period<1 ? 9 : cmo_period);
         //--- smoothing factor period
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(ema_period<1 ? 12 : ema_period);
         //--- horizontal shift of the indicator
         this.m_param[2].type=TYPE_INT;
         this.m_param[2].integer_value=ma_shift;
         //--- price type or handle
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu,%lu)",symbol_period,(current ? "" : ":"),cmo_period,ema_period);
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("VIDYA");
      this.SetDescription("Variable Index Dynamic Average");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
      //--- Write shift to buffer 0
      this.SetBufferShift(0,ma_shift);
     }
  };
//+------------------------------------------------------------------+
//| Volumes indicator class                                          |
//+------------------------------------------------------------------+
class CIndVolumes : public CIndMSTF
  {
public:
//--- Constructor
   CIndVolumes(const string symbol,const ENUM_TIMEFRAMES timeframe,
               const ENUM_APPLIED_VOLUME applied_volume  // volume type
              ) : CIndMSTF(IND_VOLUMES,1,symbol,timeframe)
     {
      //--- Set the size of the parameter array and fill it
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         //--- volume type
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=applied_volume;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Create description of parameters
      //--- If non-current chart symbol or period, their descriptions are added to parameters
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=(current ? "" : StringFormat("(%s)",symbol_period));
      //--- Write description of parameters, indicator name, its description, title, category and Digits
      this.SetParameters(param);
      this.SetName("Volumes");
      this.SetDescription("Volumes");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_VOLUME;
      this.m_digits=0;
      //--- Write description of line buffers
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Custom indicator class                                           |
//+------------------------------------------------------------------+
class CIndCustom : public CIndMSTF
  {
public:
//--- Constructor
   CIndCustom(const string symbol,const ENUM_TIMEFRAMES timeframe,
              const string path,                      // path to the indicator (for example, "Examples\\MACD.ex5")
              const string name,                      // name of the custom indicator
              const uint   buffers,                   // number of indicator buffers
              const MqlParam &param[]                 // array of custom indicator parameters
             ) : CIndMSTF(IND_CUSTOM,buffers,symbol,timeframe)
     {
      //--- If an empty array of parameters is passed, print this to log
      int total=(int)param.Size();
      if(total==0)
         ::PrintFormat("%s Error. Passed an empty array",__FUNCTION__);
      //--- If the array is not empty and its size is increased by 1 (the first string parameter must contain the indicator name)
      ResetLastError();
      if(total>0 && ::ArrayResize(this.m_param,total+1)==total+1)
        {
         //--- Reset data in the array and enter name (path to file and name of .ex5 file)
         ::ZeroMemory(this.m_param);
         //--- name of the custom indicator
         this.m_param[0].type=TYPE_STRING;
         this.m_param[0].string_value=path;
         //--- fill the array of indicator parameters
         for(int i=0;i<total;i++)
           {
            this.m_param[i+1].type=param[i].type;
            this.m_param[i+1].double_value=param[i].double_value;
            this.m_param[i+1].integer_value=param[i].integer_value;
            this.m_param[i+1].string_value=param[i].string_value;
           }
         //--- Create description of parameters
         //--- If non-current chart symbol or period, their descriptions are added to parameters
         bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
         string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
         string param=(current ? "" : StringFormat("(%s)",symbol_period));
         //--- Write description of parameters, indicator name, its description, title and category
         this.SetParameters(param);
         this.SetName(name);
         this.SetDescription(name);
         this.m_title=this.Name()+this.Parameters();
         this.m_category=IND_CATEGORY_CUSTOM;
         //--- Write a description of the first line buffer
         this.SetBufferDescription(0,this.m_title);
        }
     }
  };

所呈现的列表包含用于创建客户端终端中可用的所有技术指标的所有类,并且另外提供用于创建自定义指标的类。所有类都是相同的,它们的要点都在类代码中注释。

请注意,测试涵盖了上述一些指标的创建和运行。也就是说,只有基于在主图表上绘制一条线的移动平均线的指标才测试过。在接下来的文章中,我们将编写用于创建所有类型技术指标的多交易品种、多周期版本的模板。

事实上,创建多指标,一切都已经准备好了。但我们将超越这一想法,创建一个方便的工具,用于自动创建和使用多交易品种、多周期指标。这将是一个指标的集合类,允许您轻松创建基于标准或自定义指标的任何指标,将其转换为多交易品种、多周期的指标。


指标集合类

指标集合类本质上是指向对象的指针的常规列表。在此列表中,我们将添加用于方便地创建指标和将新创建的指标添加到集合的方法。还可以容易地获得指向所需指标的指针,并使用其中的必要数据。将新指标添加到集合时,将首先检查集合中是否存在完全相同的指标。如果集合中已经存在相同的指标,则将删除新创建的对象,并返回指向集合中存在的对象的指针。这避免了两个不同对象引用同一计算部分的情况。

集合类的结构将重复基本的多交易品种多周期指标类的结构。唯一的区别是,大多数方法必须首先通过其句柄找到所需的指标,然后调用相关方法来设置或获取其值或操作结果。

我们将使用相同的文件\MQL5\Include\IndMSTF\IndMSTF.mqh,并将继续添加一个新类。对于它的操作,您将需要包括类的文件以及指向CObject类实例及其派生类的指针的动态数组CArrayObj

//+------------------------------------------------------------------+
//| Indicator collection class                                       |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
class CMSTFIndicators
  {
private:

public:
  
  }

在类主体中,添加列表对象用于类操作的方法:

//+------------------------------------------------------------------+
//| Indicator collection class                                       |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
class CMSTFIndicators
  {
private:
   CArrayObj         m_list;
//--- Creates an indicator for the passed object
   bool              CreateIndicator(CIndMSTF *ind_obj);
//--- Adds the specified indicator to the collection
   int               AddNewIndicator(CIndMSTF *ind_obj,const string source);

public:
//--- Returns (1) indicator object by handle, (2) number of indicators in the collection
   CIndMSTF         *GetIndicatorObj(const int ind_handle,const string source) const;
   uint              IndicatorsTotal(void)                  const { return this.m_list.Total(); }

//--- Populates buffers of (1) the indicator by handle, (2) all indicators in the collection
   bool              Calculate(const int ind_handle);
   bool              Calculate(void);
//--- Sets the (1) specified, (2) default description of the indicator buffer line
   void              SetPlotLabel(const uint plot_index,const string descript);
   void              SetPlotLabelFromBuffer(const uint plot_index,const int ind_handle,const uint buffer_num);
//--- Sets the shift to the specified plotting buffer
   void              SetPlotShift(const uint plot_index,const int shift);
//--- (1) Sets (2) returns the initializing value of the given buffer specified by the indicator handle
   void              SetBufferInitValue(const int ind_handle,const uint buffer_num,const double value);
   double            BufferInitValue(const int ind_handle,const uint buffer_num) const;

//--- Returns indicator data by handle from the specified buffer at index (1) as is, (2) for the specified symbol/timeframe
   double            GetData(const int ind_handle,const uint buffer_num,const uint index);
   double            GetDataTo(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const int ind_handle,const uint buffer_num,const uint index);

//--- (1) Copies data of the specified calculation part buffer of the indicator by handle into the indicator buffer, taking into account chart symbol/period,
//--- (2) returns the amount of data in the specified buffer of the indicator by handle
   bool              DataToBuffer(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const int ind_handle,const uint buffer_num,const int limit,double &buffer[]);
   uint              DataTotal(const int ind_handle,const uint buffer_num) const;

   //--- Returns (1) buffer description, (2) state of the line data of given buffer of indicator specified by handle on the specified bar
   //--- (3) indicator line state for the specific chart symbol/period, (4) indicator line state relation to the specified level,
   //--- (5)  state of relation of indicator line with specified level for certain chart symbol/period, (6) indicator category description
   string            BufferDescription(const int ind_handle,const uint buffer_num);
   ENUM_LINE_STATE   BufferLineState(const int ind_handle,const uint buffer_num,const int index);
   ENUM_LINE_STATE   BufferLineState(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ind_handle,const uint buffer_num,const int index);
   ENUM_LINE_STATE   BufferLineStateRelative(const int ind_handle,const int buffer_num,const int index,const double level0,const double level1=EMPTY_VALUE);
   ENUM_LINE_STATE   BufferLineStateRelative(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ind_handle,const int buffer_num,const int index,const double level0,const double level1=EMPTY_VALUE);
   string            CategoryDescription(const int ind_handle);
   
//--- Sets (1) identifier, (2) Digits, (3) user description, (4) buffer description
   void              SetID(const int ind_handle,const int id);
   void              SetDigits(const int ind_handle,const int digits);
   void              SetDescription(const int ind_handle,const string descr);
   void              SetBufferDescription(const int ind_handle,const uint buffer_num,const string descr);

//--- Returns flag of whether the buffer is set as series, (2) historical data for symbol/period is synchronized
   bool              IsSeries(const int ind_handle,const uint buffer_num) const;
   bool              IsSynchronized(const int ind_handle) const;
   
//--- Returns (1) timeframe, (2) symbol, (3) name, (4) list of parameters, (5) handle, (6) Digits
//--- number of (7) buffers, (8) bars, (9) identifier, (10) description, (11) title, (12) category,
//--- (13) number of parameters, (14) program type, description of (15) category, (16) indicator buffer
   ENUM_TIMEFRAMES   Timeframe(const int ind_handle) const;
   string            Symbol(const int ind_handle) const;
   string            Name(const int ind_handle) const;
   string            Parameters(const int ind_handle) const;
   int               Digits(const int ind_handle) const;
   uint              BuffersTotal(const int ind_handle) const;
   uint              RatesTotal(const int ind_handle) const;
   int               ID(const int ind_handle) const;
   string            Description(const int ind_handle) const;
   string            Title(const int ind_handle) const;
   ENUM_IND_CATEGORY Category(const int ind_handle) const;
   uint              ParamsTotal(const int ind_handle) const;
//--- Returns (1) structure of parameters by index from array, (2) timeframe description
   MqlParam          GetMqlParam(const int ind_handle,const int index) const;
   string            TimeframeDescription(const int ind_handle)    const;
//--- Returns amount of calculated data
   int               Calculated(const int ind_handle) const;
   
//--- Virtual method returning the type of object (indicator)
      ENUM_INDICATOR    Type(const int ind_handle) const;
   
//--- Methods for adding indicators to the collection
   int               AddNewAC(const string symbol,const ENUM_TIMEFRAMES timeframe);
   int               AddNewAD(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK);
   int               AddNewADX(const string symbol,const ENUM_TIMEFRAMES timeframe,const int adx_period=14);
   int               AddNewADXWilder(const string symbol,const ENUM_TIMEFRAMES timeframe,const int adx_period=14);
   int               AddNewAlligator(const string symbol,const ENUM_TIMEFRAMES timeframe,const int jaw_period=13,
                                                                                         const int jaw_shift=8,
                                                                                         const int teeth_period=8,
                                                                                         const int teeth_shift=5,
                                                                                         const int lips_period=5,
                                                                                         const int lips_shift=3,
                                                                                         const ENUM_MA_METHOD ma_method=MODE_SMMA,
                                                                                         const ENUM_APPLIED_PRICE applied_price=PRICE_MEDIAN);
   int               AddNewAMA(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ama_period=9,
                                                                                   const int fast_ma_period=2,
                                                                                   const int slow_ma_period=30,
                                                                                   const int ama_shift=0,
                                                                                   const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE);
   int               AddNewAO(const string symbol,const ENUM_TIMEFRAMES timeframe);
   int               AddNewATR(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14);
   int               AddNewBearsPower(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=13);
   int               AddNewBullsPower(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=13);
   int               AddNewBands(const string symbol,const ENUM_TIMEFRAMES timeframe,const int bands_period=20,
                                                                                     const int bands_shift=0,
                                                                                     const double deviation=2.0,
                                                                                     const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE);
   int               AddNewCCI(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14,
                                                                                   const ENUM_APPLIED_PRICE applied_price=PRICE_TYPICAL);
   int               AddNewChaikin(const string symbol,const ENUM_TIMEFRAMES timeframe,const int fast_ma_period=3,
                                                                                       const int slow_ma_period=10,
                                                                                       const ENUM_MA_METHOD ma_method=MODE_EMA,
                                                                                       const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK);
   int               AddNewDEMA(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14,
                                                                                    const int ma_shift=0,
                                                                                    const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE);
   int               AddNewDeMarker(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14);
   int               AddNewEnvelopes(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14,
                                                                                         const int ma_shift=0,
                                                                                         const ENUM_MA_METHOD ma_method=MODE_SMA,
                                                                                         const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE,
                                                                                         const double deviation=0.1);
   int               AddNewForce(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=13,
                                                                                     const ENUM_MA_METHOD ma_method=MODE_SMA,
                                                                                     const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK);
   int               AddNewFractals(const string symbol,const ENUM_TIMEFRAMES timeframe);
   int               AddNewFrAMA(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14,
                                                                                     const int ma_shift=0,
                                                                                     const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE);
   int               AddNewGator(const string symbol,const ENUM_TIMEFRAMES timeframe,const int jaw_period=13,
                                                                                     const int jaw_shift=8,
                                                                                     const int teeth_period=8,
                                                                                     const int teeth_shift=5,
                                                                                     const int lips_period=5,
                                                                                     const int lips_shift=3,
                                                                                     const ENUM_MA_METHOD ma_method=MODE_SMMA,
                                                                                     const ENUM_APPLIED_PRICE applied_price=PRICE_MEDIAN);
   int               AddNewIchimoku(const string symbol,const ENUM_TIMEFRAMES timeframe,const int tenkan_sen=9,
                                                                                        const int kijun_sen=26,
                                                                                        const int senkou_span_b=52);
   int               AddNewBWMFI(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK);
   int               AddNewMomentum(const string symbol,const ENUM_TIMEFRAMES timeframe,const int mom_period=14,
                                                                                        const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE);
   int               AddNewMFI(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14,
                                                                                   const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK);
   int               AddNewMA(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=10,
                                                                                  const int ma_shift=0,
                                                                                  const ENUM_MA_METHOD ma_method=MODE_SMA,
                                                                                  const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE);
   int               AddNewOsMA(const string symbol,const ENUM_TIMEFRAMES timeframe,const int fast_ema_period=12,
                                                                                    const int slow_ema_period=26,
                                                                                    const int signal_period=9,
                                                                                    const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE);
   int               AddNewMACD(const string symbol,const ENUM_TIMEFRAMES timeframe,const int fast_ema_period=12,
                                                                                    const int slow_ema_period=26,
                                                                                    const int signal_period=9,
                                                                                    const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE);
   int               AddNewOBV(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK);
   int               AddNewSAR(const string symbol,const ENUM_TIMEFRAMES timeframe,const double step=0.02,
                                                                                   const double maximum=0.2);
   int               AddNewRSI(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14,
                                                                                   const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE);
   int               AddNewRVI(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=10);
   int               AddNewStdDev(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=20,
                                                                                      const int ma_shift=0,
                                                                                      const ENUM_MA_METHOD ma_method=MODE_SMA,
                                                                                      const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE);
   int               AddNewStochastic(const string symbol,const ENUM_TIMEFRAMES timeframe,const int Kperiod=5,
                                                                                          const int Dperiod=3,
                                                                                          const int slowing=3,
                                                                                          const ENUM_MA_METHOD ma_method=MODE_SMA,
                                                                                          const ENUM_STO_PRICE price_field=STO_LOWHIGH);
   int               AddNewTEMA(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14,
                                                                                    const int ma_shif=0,
                                                                                    const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE);
   int               AddNewTriX(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14,
                                                                                    const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE);
   int               AddNewWPR(const string symbol,const ENUM_TIMEFRAMES timeframe,const int calc_period=14);
   int               AddNewVIDyA(const string symbol,const ENUM_TIMEFRAMES timeframe,const int cmo_period=9,
                                                                                     const int ema_period=12,
                                                                                     const int ma_shift=0,
                                                                                     const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE);
   int               AddNewVolumes(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK);
   int               AddNewCustom(const string symbol,const ENUM_TIMEFRAMES timeframe,const string path,       // path to the indicator (for example, "Examples\\MACD.ex5")
                                                                                      const string name,       // name of custom indicator (for example, "Custom MACD")
                                                                                      const uint   buffers,    // number of buffers
                                                                                      const MqlParam &param[]);// array of parameters
//--- Timer
   void OnTimer(void)
     {
      //--- In a loop through all indicators form the collection
      int total=this.m_list.Total();
      for(int i=0;i<total;i++)
        {
         //--- get a pointer to the next indicator object
         //--- and call its timer
         CIndMSTF *obj=this.m_list.At(i);
         if(obj!=NULL)
            obj.OnTimer();
        }
     }
//--- Constructor/destructor
                     CMSTFIndicators(void){ this.m_list.Clear(); }
                    ~CMSTFIndicators(void){;}
  };


操作列表中指标的方法的实现:

//+------------------------------------------------------------------+
//| Creates indicator calculation part for the passed object         |
//+------------------------------------------------------------------+
bool CMSTFIndicators::CreateIndicator(CIndMSTF *ind_obj)
  {
   //--- If the calculation part of the indicator could not be created
   if(!ind_obj.CreateIndicator())
     {
      //--- look for the index of the indicator object in the collection list
      //--- using the index, delete the indicator object from the collection list
      this.m_list.Sort();
      int index=this.m_list.Search(ind_obj);
      this.m_list.Delete(index);
      //--- Return false
      return false;
     }
//--- The calculation part has been successfully created - return true
   return true;
  }
//+------------------------------------------------------------------+
//| Returns indicator object by the calculation part handle          |
//+------------------------------------------------------------------+
CIndMSTF *CMSTFIndicators::GetIndicatorObj(const int ind_handle,const string source) const
  {
//--- If an invalid handle is passed to the method, report this and return NULL
   if(ind_handle==INVALID_HANDLE)
     {
      ::PrintFormat("%s: Error handle",source);
      return NULL;
     }
//--- In a loop through all indicator objects in the collection list
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      //--- get a pointer to the next indicator object
      CIndMSTF *obj=this.m_list.At(i);
      if(obj==NULL)
         continue;
      //--- If the indicator handle is equal to that passed to the method -
      //--- return a pointer to the found indicator object
      if(obj.Handle()==ind_handle)
         return obj;
     }
//--- Nothing is found - return NULL
   return NULL;
  }
//+------------------------------------------------------------------+
//| Populate buffers of the indicator at the handle                  |
//+------------------------------------------------------------------+
bool CMSTFIndicators::Calculate(const int ind_handle)
  {
   //--- Get a pointer to an indicator object using the handle
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
      return false;
   //--- Return the result of the Calculate method obtained from the handle of the indicator object
   return obj.Calculate();
  }
//+------------------------------------------------------------------+
//| Populate buffers of all indicators in the collection             |
//+------------------------------------------------------------------+
bool CMSTFIndicators::Calculate(void)
  {
   //--- Declare the variable for storing the result
   bool res=true;
//--- In a loop through all indicator objects in the collection list
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      //--- get a pointer to the next indicator object
      CIndMSTF *obj=this.m_list.At(i);
      if(obj==NULL)
         continue;
      //--- Add to the 'res' variable the result of calling the Calculate method of the next indicator object
      res &=obj.Calculate();
      //--- If the method worked with an error, inform that in the journal
      if(!res)
         ::PrintFormat("%s::%s: Error in indicator calculation: %s",__FUNCTION__,obj.Title(),TypeErrorcDescription(obj.TypeError()));
     }
//--- If the overall result is false, inform of that in the journal
   if(!res)
      ::PrintFormat("%s: Not all indicators have been calculated successfully. It is necessary to recalculate the buffers of all indicators",__FUNCTION__);
//--- Return the result of calling the Calculate methods of all indicators in the collection
   return res;
  }
//+------------------------------------------------------------------+
//| Returns data of the indicator at the handle                      |
//| from the specified buffers by index as is                        |
//+------------------------------------------------------------------+
double CMSTFIndicators::GetData(const int ind_handle,const uint buffer_num,const uint index)
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return EMPTY_VALUE;
     }
//--- Return data from the specified indicator buffer at the index passed to the method
   return obj.GetData(buffer_num,index);
  }
//+------------------------------------------------------------------+
//| Returns data of the indicator at the handle                      |
//| from the specified buffer at index for this symbol/timeframe     |
//+------------------------------------------------------------------+
double CMSTFIndicators::GetDataTo(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const int ind_handle,const uint buffer_num,const uint index)
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return EMPTY_VALUE;
     }
//--- Return data from the specified indicator buffer at the index passed to the method
   return obj.GetDataTo(symbol_to,timeframe_to,buffer_num,index);
  }
//+------------------------------------------------------------------+
//| Fills the passed indicator buffer with data                      |
//+------------------------------------------------------------------+
bool CMSTFIndicators::DataToBuffer(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const int ind_handle,const uint buffer_num,const int limit,double &buffer[])
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return false;
     }
//--- Fill the buffer array passed to the method from the specified indicator buffer
   return obj.DataToBuffer(symbol_to,timeframe_to,buffer_num,limit,buffer);
  }
//+------------------------------------------------------------------+
//| Set the specified description for the buffer line                |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetPlotLabel(const uint plot_index,const string descript)
  {
   ::PlotIndexSetString(plot_index,PLOT_LABEL,descript);
  }
//+------------------------------------------------------------------+
//| Set default description for the buffer line                      |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetPlotLabelFromBuffer(const uint plot_index,const int ind_handle,const uint buffer_num)
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return;
     }
//--- Set the description of the specified indicator buffer to the specified plotting buffer
   ::PlotIndexSetString(plot_index,PLOT_LABEL,obj.BufferDescription(buffer_num));
  }
//+------------------------------------------------------------------+
//| Set the shift for the specified plotting buffer                  |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetPlotShift(const uint plot_index,const int shift)
  {
   ::PlotIndexSetInteger(plot_index,PLOT_SHIFT,shift);
  }
//+------------------------------------------------------------------+
//| Return the description of the given buffer                       |
//| of the indicator specified by handle                             |
//+------------------------------------------------------------------+
string CMSTFIndicators::BufferDescription(const int ind_handle,const uint buffer_num)
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
//--- If pointer to object received, return description of the specified buffer from it. Otherwise error text
   return(obj!=NULL ? obj.BufferDescription(buffer_num) : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__));
  }
//+------------------------------------------------------------------+
//| Set the initializing value for the specified buffer              |
//| of the indicator specified by handle                             |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetBufferInitValue(const int ind_handle,const uint buffer_num,const double value)
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return;
     }
//--- Set the specified initializing "empty" value for the specified buffer
   obj.SetBufferInitValue(buffer_num,value);
  }
//+------------------------------------------------------------------+
//| Return the initialization value of the specified buffer          |
//| of the indicator specified by handle                             |
//+------------------------------------------------------------------+
double CMSTFIndicators::BufferInitValue(const int ind_handle,const uint buffer_num) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return WRONG_VALUE;
     }
//--- Return the initializing "empty" value set for the specified buffer
   return obj.BufferInitValue(buffer_num);
  }
//+------------------------------------------------------------------+
//| Returns the line data state of the given buffer                  |
//| specified by indicator handle at the specified bar               |
//+------------------------------------------------------------------+
ENUM_LINE_STATE CMSTFIndicators::BufferLineState(const int ind_handle,const uint buffer_num,const int index)
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return LINE_STATE_NONE;
     }
//--- Return the state of the line of the specified buffer at the specified index
   return obj.BufferLineState(buffer_num,index);
  }
//+------------------------------------------------------------------+
//| Returns the line data state of the given buffer                  |
//| specified by indicator handle at the symbol/timeframe bar        |
//+------------------------------------------------------------------+
ENUM_LINE_STATE CMSTFIndicators::BufferLineState(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ind_handle,const uint buffer_num,const int index)
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return LINE_STATE_NONE;
     }
//--- Get the time of the passed to the method
   datetime array[1];
   if(::CopyTime(symbol,timeframe,index,1,array)!=1)
     {
      ::PrintFormat("%s::%s: Failed to get the time of the bar with index %ld. Error %lu",__FUNCTION__,obj.Title(),index,::GetLastError());
      return LINE_STATE_NONE;
     }
//--- Get the bar index in the indicator object buffer corresponding to the found time
   int bar=::iBarShift(obj.Symbol(),obj.Timeframe(),array[0]);
//--- If a bar is found, return the line state on the found bar, otherwise an undefined state
   return(bar!=WRONG_VALUE ? obj.BufferLineState(buffer_num,bar) : LINE_STATE_NONE);
  }
//+------------------------------------------------------------------+
//| Return the line data ratio for the given buffer                  |
//| specified by indicator handle at the specified bar               |
//| with specified valiues                                           |
//+------------------------------------------------------------------+
ENUM_LINE_STATE CMSTFIndicators::BufferLineStateRelative(const int ind_handle,const int buffer_num,const int index,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return LINE_STATE_NONE;
     }
//--- Return the ratio of the indicator line and the level in the specified buffer at the specified index
   return obj.BufferLineStateRelative(buffer_num,index,level0,level1);
  }
//+------------------------------------------------------------------+
//| Return the line data ratio for the given buffer                  |
//| specified by indicator handle at the specified bar               |
//| with the specified values on the specified chart symbol/period   |
//+------------------------------------------------------------------+
ENUM_LINE_STATE CMSTFIndicators::BufferLineStateRelative(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ind_handle,const int buffer_num,const int index,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return LINE_STATE_NONE;
     }
//--- Return the ratio of the indicator line and the level in the specified buffer at the specified index
   return obj.BufferLineStateRelative(symbol,timeframe,buffer_num,index,level0,level1);
  }
//+------------------------------------------------------------------+
//| Return category description                                      |
//+------------------------------------------------------------------+
string CMSTFIndicators::CategoryDescription(const int ind_handle)
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
//--- If the object is received, return the description of the category Otherwise error text
   return(obj!=NULL ? obj.CategoryDescription() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__));
  }
//+------------------------------------------------------------------+
//| Set identifier                                                   |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetID(const int ind_handle,const int id)
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return;
     }
//--- Set the identifier for the received object
   obj.SetID(id);
  }
//+------------------------------------------------------------------+
//| Set Digits of the indicator                                      |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetDigits(const int ind_handle,const int digits)
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return;
     }
//--- Set Digits for the received object
   obj.SetDigits(digits);
  }
//+------------------------------------------------------------------+
//| Set a custom description                                         |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetDescription(const int ind_handle,const string descr)
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return;
     }
//--- Set a description for the received object
   obj.SetDescription(descr);
  }
//+------------------------------------------------------------------+
//| Set a description of the specified buffer                        |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetBufferDescription(const int ind_handle,const uint buffer_num,const string descr)
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return;
     }
//--- Set a description for the specified buffer of the received object
   obj.SetBufferDescription(buffer_num,descr);
  }
//+------------------------------------------------------------------+
//| Returns the timeseries flag of the given buffer                  |
//+------------------------------------------------------------------+
bool CMSTFIndicators::IsSeries(const int ind_handle,const uint buffer_num) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return false;
     }
//--- Return the timeseries flag of the specified buffer of the received object
   return obj.IsSeries(buffer_num);
  }
//+------------------------------------------------------------------+
//| Returns the synchronization flag for                             |
//| historical data for the symbol/period                            |
//+------------------------------------------------------------------+
bool CMSTFIndicators::IsSynchronized(const int ind_handle) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return false;
     }
//--- Return the synchronization flag of the received object
   return obj.IsSynchronized();
  }
//+------------------------------------------------------------------+
//| Return the timeframe of the specified indicator                  |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES CMSTFIndicators::Timeframe(const int ind_handle) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return WRONG_VALUE;
     }
//--- Return the timeframe of the received object
   return obj.Timeframe();
  }
//+------------------------------------------------------------------+
//| Returns the symbol of the specified indicator                    |
//+------------------------------------------------------------------+
string CMSTFIndicators::Symbol(const int ind_handle) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
//--- If the object is received, return the name of the symbol Otherwise error text
   return(obj!=NULL ? obj.Symbol() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__));
  }
//+------------------------------------------------------------------+
//| Return the name of the specified indicator                       |
//+------------------------------------------------------------------+
string CMSTFIndicators::Name(const int ind_handle) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
//--- If the object is received, return the name of the indicator Otherwise error text
   return(obj!=NULL ? obj.Name() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__));
  }
//+------------------------------------------------------------------+
//| Returns a list of parameters of the specified indicator          |
//+------------------------------------------------------------------+
string CMSTFIndicators::Parameters(const int ind_handle) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
//--- If the object is received, return a list of indicator parameters Otherwise error text
   return(obj!=NULL ? obj.Parameters() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__));
  }
//+------------------------------------------------------------------+
//| Return Digits of the specified indicator                         |
//+------------------------------------------------------------------+
int CMSTFIndicators::Digits(const int ind_handle) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return WRONG_VALUE;
     }
//--- Return Digits of the received object
   return obj.Digits();
  }
//+------------------------------------------------------------------+
//| Return the number of buffers of the specified indicator          |
//+------------------------------------------------------------------+
uint CMSTFIndicators::BuffersTotal(const int ind_handle) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return 0;
     }
//--- Return the number of buffers of the received object
   return obj.BuffersTotal();
  }
//+------------------------------------------------------------------+
//| Return the number of timeseries bars for specified the indicator |
//+------------------------------------------------------------------+
uint CMSTFIndicators::RatesTotal(const int ind_handle) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return 0;
     }
//--- Return the number of bars in the timeseries of the received object
   return obj.RatesTotal();
  }
//+------------------------------------------------------------------+
//| Return the identifier of the specified indicator                 |
//+------------------------------------------------------------------+
int CMSTFIndicators::ID(const int ind_handle) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return WRONG_VALUE;
     }
//--- Return the identifier of the received object
   return obj.ID();
  }
//+------------------------------------------------------------------+
//| Return a description of the specified indicator                  |
//+------------------------------------------------------------------+
string CMSTFIndicators::Description(const int ind_handle) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
//--- If the object is received, return the indicator description Otherwise error text
   return(obj!=NULL ? obj.Description() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__));
  }
//+------------------------------------------------------------------+
//| Return the title of the specified indicator                      |
//+------------------------------------------------------------------+
string CMSTFIndicators::Title(const int ind_handle) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
//--- If the object is received, return the indicator title Otherwise error text
   return(obj!=NULL ? obj.Title() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__));
  }
//+------------------------------------------------------------------+
//| Return the category of the specified indicator                   |
//+------------------------------------------------------------------+
ENUM_IND_CATEGORY CMSTFIndicators::Category(const int ind_handle) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return IND_CATEGORY_NONE;
     }
//--- Return the category of the received object
   return obj.Category();
  }
//+------------------------------------------------------------------+
//| Return the number of parameters of the specified indicator       |
//+------------------------------------------------------------------+
uint CMSTFIndicators::ParamsTotal(const int ind_handle) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return 0;
     }
//--- Return the number of parameters of the received object
   return obj.ParamsTotal();
  }
//+------------------------------------------------------------------+
//| Return the structure of parameters by index from the array       |
//| for the specified indicator                                      |
//+------------------------------------------------------------------+
MqlParam CMSTFIndicators::GetMqlParam(const int ind_handle,const int index) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      MqlParam null;
      ::ZeroMemory(null);
      return null;
     }
//--- Return the structure of parameters of the received object by index from the array of parameters
   return obj.GetMqlParam(index);
  }
//+------------------------------------------------------------------+
//| Return a timeframe description for the specified indicator       |
//+------------------------------------------------------------------+
string CMSTFIndicators::TimeframeDescription(const int ind_handle) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
//--- If the object is received, return a description of the indicator timeframe Otherwise error text
   return(obj!=NULL ? obj.Description() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__));
  }
//+------------------------------------------------------------------+
//| Return the amount of calculated data of the specified indicator  |
//+------------------------------------------------------------------+
int CMSTFIndicators::Calculated(const int ind_handle) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return WRONG_VALUE;
     }
//--- Return the amount of calculated data of the received object
   return obj.Calculated();
  }
//+------------------------------------------------------------------+
//| Return the type of the specified indicator                       |
//+------------------------------------------------------------------+
ENUM_INDICATOR CMSTFIndicators::Type(const int ind_handle) const
  {
//--- Get a pointer to the indicator object using the handle passed to the method
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return (ENUM_INDICATOR)WRONG_VALUE;
     }
//--- Return the indicator type of the received object
   return (ENUM_INDICATOR)obj.Type();
  }

所有方法的逻辑都在方法列表中进行了注释。首先,我们获取一个指向列表中所需指标的指针,然后设置或返回其属性,或者执行计算并返回结果。以上所有调用的方法都在讨论多交易品种、多周期指标的基类时讨论了。

创建新指标对象的方法的实现:

//+------------------------------------------------------------------+
//| Add the specified indicator to the collection                    |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewIndicator(CIndMSTF *ind_obj,const string source)
  {
//--- Set the sorted list flag to the collection list
   this.m_list.Sort();
//--- Search the list for an index matching the indicator object passed to the method
   int index=this.m_list.Search(ind_obj);
//--- If such an indicator with the same parameters is already in the list,
   if(index>WRONG_VALUE)
     {
      //--- report this to journal and delete the new indicator object
      ::PrintFormat("%s: The %s indicator with such parameters %s is already in the collection",source,ind_obj.Name(),ind_obj.Parameters());
      delete ind_obj;
      //--- Get a pointer to an already existing indicator object in the list and return its handle
      ind_obj=this.m_list.At(index);
      return(ind_obj!=NULL ? ind_obj.Handle() : INVALID_HANDLE);
     }
//--- If such an indicator is not in the list, but it could not be placed in the list
   if(!this.m_list.Add(ind_obj))
     {
      //--- report the error in the journal, delete the indicator object and return INVALID_HANDLE
      ::PrintFormat("%s: Error. Failed to add %s indicator to collection",source,ind_obj.Name());
      delete ind_obj;
      return INVALID_HANDLE;
     }
//--- If indicator is placed in list, but creating a calculation part for it failed, return INVALID_HANDLE
//--- (if there is an error creating a calculation part, the indicator object is deleted in the CreateIndicator method)
   if(!this.CreateIndicator(ind_obj))
      return INVALID_HANDLE;
//--- Successful - inform about addition of a new indicator to collection and return its handle
   ::PrintFormat("%s: %s indicator (handle %ld) added to the collection",source,ind_obj.Title(),ind_obj.Handle());
   return ind_obj.Handle();
  }
//+------------------------------------------------------------------+
//| Add the Accelerator Oscillator indicator to the collection       |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewAC(const string symbol,const ENUM_TIMEFRAMES timeframe)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndAC *ind_obj=new CIndAC(symbol,timeframe);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create AC indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Accumulation/Distribution indicator to the collection     |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewAD(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndAD *ind_obj=new CIndAD(symbol,timeframe,applied_volume);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create A/D indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+--------------------------------------------------------------------+
//| Add Average Directional Movement Index to the collection           |
//+--------------------------------------------------------------------+
int CMSTFIndicators::AddNewADX(const string symbol,const ENUM_TIMEFRAMES timeframe,const int adx_period=14)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndADX *ind_obj=new CIndADX(symbol,timeframe,adx_period);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create ADX indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add to the collection the indicator                              |
//| Average Directional Movement Index by Welles Wilder              |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewADXWilder(const string symbol,const ENUM_TIMEFRAMES timeframe,const int adx_period=14)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndADXW *ind_obj=new CIndADXW(symbol,timeframe,adx_period);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create ADX Wilder indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Alligator indicator to the collection                    |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewAlligator(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                      const int jaw_period=13,
                                      const int jaw_shift=8,
                                      const int teeth_period=8,
                                      const int teeth_shift=5,
                                      const int lips_period=5,
                                      const int lips_shift=3,
                                      const ENUM_MA_METHOD ma_method=MODE_SMMA,
                                      const ENUM_APPLIED_PRICE applied_price=PRICE_MEDIAN)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndAlligator *ind_obj=new CIndAlligator(symbol,timeframe,jaw_period,jaw_shift,teeth_period,teeth_shift,lips_period,lips_shift,ma_method,applied_price);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create Alligator indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Adaptive Moving Average indicator to the collection      |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewAMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                const int ama_period=9,
                                const int fast_ma_period=2,
                                const int slow_ma_period=30,
                                const int ama_shift=0,
                                const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndAMA *ind_obj=new CIndAMA(symbol,timeframe,ama_period,fast_ma_period,slow_ma_period,ama_shift,applied_price);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create AMA indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Awesome Oscillator indicator to the collection           |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewAO(const string symbol,const ENUM_TIMEFRAMES timeframe)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndAO *ind_obj=new CIndAO(symbol,timeframe);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create AO indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Average True Range indicator to the collection           |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewATR(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndATR *ind_obj=new CIndATR(symbol,timeframe,ma_period);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create ATR indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Bears Power indicator to the collection                  |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewBearsPower(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=13)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndBears *ind_obj=new CIndBears(symbol,timeframe,ma_period);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create Bears indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Bulls Power indicator to the collection                  |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewBullsPower(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=13)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndBulls *ind_obj=new CIndBulls(symbol,timeframe,ma_period);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create Bulls indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Bollinger Bands® indicator to the collection             |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewBands(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                  const int bands_period=20,
                                  const int bands_shift=0,
                                  const double deviation=2.0,
                                  const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndBands *ind_obj=new CIndBands(symbol,timeframe,bands_period,bands_shift,deviation,applied_price);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create Bands indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Commodity Channel Index indicator to the collection      |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewCCI(const string symbol,const ENUM_TIMEFRAMES timeframe,
                               const int ma_period=14,
                               const ENUM_APPLIED_PRICE applied_price=PRICE_TYPICAL)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndCCI *ind_obj=new CIndCCI(symbol,timeframe,ma_period,applied_price);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create CCI indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Chaikin Oscillator indicator to the collection           |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewChaikin(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                    const int fast_ma_period=3,
                                    const int slow_ma_period=10,
                                    const ENUM_MA_METHOD ma_method=MODE_EMA,
                                    const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndCHO *ind_obj=new CIndCHO(symbol,timeframe,fast_ma_period,slow_ma_period,ma_method,applied_volume);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create Chaikin indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+-------------------------------------------------------------------+
//| Add Double Exponential Moving Average to the collection           |
//+-------------------------------------------------------------------+
int CMSTFIndicators::AddNewDEMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                const int ma_period=14,
                                const int ma_shift=0,
                                const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndDEMA *ind_obj=new CIndDEMA(symbol,timeframe,ma_period,ma_shift,applied_price);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create DEMA indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the DeMarker indicator to the collection                     |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewDeMarker(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndDeM *ind_obj=new CIndDeM(symbol,timeframe,ma_period);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create DeMarker indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Envelopes indicator to the collection                    |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewEnvelopes(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                      const int ma_period=14,
                                      const int ma_shift=0,
                                      const ENUM_MA_METHOD ma_method=MODE_SMA,
                                      const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE,
                                      const double deviation=0.1)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndEnvelopes *ind_obj=new CIndEnvelopes(symbol,timeframe,ma_method,ma_shift,ma_method,applied_price,deviation);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create Envelopes indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Force Index indicator to the collection                  |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewForce(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                  const int ma_period=13,
                                  const ENUM_MA_METHOD ma_method=MODE_SMA,
                                  const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndForce *ind_obj=new CIndForce(symbol,timeframe,ma_period,ma_method,applied_volume);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create Force indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Fractals indicator to the collection                     |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewFractals(const string symbol,const ENUM_TIMEFRAMES timeframe)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndFractals *ind_obj=new CIndFractals(symbol,timeframe);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create Fractals indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Fractal Adaptive Moving Average indicator to collection  |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewFrAMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                 const int ma_period=14,
                                 const int ma_shift=0,
                                 const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndFrAMA *ind_obj=new CIndFrAMA(symbol,timeframe,ma_period,ma_shift,applied_price);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create FrAMA indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Gator indicator to the collection                        |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewGator(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                  const int jaw_period=13,
                                  const int jaw_shift=8,
                                  const int teeth_period=8,
                                  const int teeth_shift=5,
                                  const int lips_period=5,
                                  const int lips_shift=3,
                                  const ENUM_MA_METHOD ma_method=MODE_SMMA,
                                  const ENUM_APPLIED_PRICE applied_price=PRICE_MEDIAN)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndGator *ind_obj=new CIndGator(symbol,timeframe,jaw_period,jaw_shift,teeth_period,teeth_shift,lips_period,lips_shift,ma_method,applied_price);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create Gator indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Ichimoku Kinko Hyo indicator to the collection           |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewIchimoku(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                    const int tenkan_sen=9,
                                    const int kijun_sen=26,
                                    const int senkou_span_b=52)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndIchimoku *ind_obj=new CIndIchimoku(symbol,timeframe,tenkan_sen,kijun_sen,senkou_span_b);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create Ichimoku indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Market Facilitation Index indicator to the collection    |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewBWMFI(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndBWMFI *ind_obj=new CIndBWMFI(symbol,timeframe,applied_volume);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create BW MFI indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Momentum indicator to the collection                     |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewMomentum(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                    const int mom_period=14,
                                    const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndMomentum *ind_obj=new CIndMomentum(symbol,timeframe,mom_period,applied_price);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create Momentum indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Money Flow Index indicator to the collection             |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewMFI(const string symbol,const ENUM_TIMEFRAMES timeframe,
                               const int ma_period=14,
                               const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndMFI *ind_obj=new CIndMFI(symbol,timeframe,ma_period,applied_volume);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create MFI indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Moving Average indicator to the collection               |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
                               const int ma_period=10,
                               const int ma_shift=0,
                               const ENUM_MA_METHOD ma_method=MODE_SMA,
                               const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndMA *ind_obj=new CIndMA(symbol,timeframe,ma_period,ma_shift,ma_method,applied_price);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create MA indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Moving Average of Oscillator indicator to the collection |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewOsMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                const int fast_ema_period=12,
                                const int slow_ema_period=26,
                                const int signal_period=9,
                                const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndOsMA *ind_obj=new CIndOsMA(symbol,timeframe,fast_ema_period,slow_ema_period,signal_period,applied_price);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create OsMA indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add to the collection the indicator                              |
//| Moving Averages Convergence/Divergence                           |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewMACD(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                const int fast_ema_period=12,
                                const int slow_ema_period=26,
                                const int signal_period=9,
                                const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndMACD *ind_obj=new CIndMACD(symbol,timeframe,fast_ema_period,slow_ema_period,signal_period,applied_price);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create MACD indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the On Balance Volume indicator to the collection            |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewOBV(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndOBV *ind_obj=new CIndOBV(symbol,timeframe,applied_volume);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create OBV indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+-------------------------------------------------------------------+
//| Add Parabolic Stop and Reverse system to the collection           |
//+-------------------------------------------------------------------+
int CMSTFIndicators::AddNewSAR(const string symbol,const ENUM_TIMEFRAMES timeframe,
                               const double step=0.02,
                               const double maximum=0.2)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndSAR *ind_obj=new CIndSAR(symbol,timeframe,step,maximum);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create SAR indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Relative Strength Index indicator to the collection      |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewRSI(const string symbol,const ENUM_TIMEFRAMES timeframe,
                               const int ma_period=14,
                               const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndRSI *ind_obj=new CIndRSI(symbol,timeframe,ma_period,applied_price);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create RSI indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Relative Vigor Index indicator to the collection         |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewRVI(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=10)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndRVI *ind_obj=new CIndRVI(symbol,timeframe,ma_period);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create RVI indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Standard Deviation indicator to the collection           |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewStdDev(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                  const int ma_period=20,
                                  const int ma_shift=0,
                                  const ENUM_MA_METHOD ma_method=MODE_SMA,
                                  const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndStdDev *ind_obj=new CIndStdDev(symbol,timeframe,ma_period,ma_shift,ma_method,applied_price);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create StdDev indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Stochastic Oscillator indicator to the collection        |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewStochastic(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                      const int Kperiod=5,
                                      const int Dperiod=3,
                                      const int slowing=3,
                                      const ENUM_MA_METHOD ma_method=MODE_SMA,
                                      const ENUM_STO_PRICE price_field=STO_LOWHIGH)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndStoch *ind_obj=new CIndStoch(symbol,timeframe,Kperiod,Dperiod,slowing,ma_method,price_field);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create Stochastic indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+-------------------------------------------------------------------+
//| Add Triple Exponential Moving Average indicator to the collection |
//+-------------------------------------------------------------------+
int CMSTFIndicators::AddNewTEMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                const int ma_period=14,
                                const int ma_shift=0,
                                const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndTEMA *ind_obj=new CIndTEMA(symbol,timeframe,ma_period,ma_shift,applied_price);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create TEMA indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
  
//+------------------------------------------------------------------+
//| Add to the collection the indicator                              |
//| Triple Exponential Moving Averages Oscillator                    |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewTriX(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                const int ma_period=14,
                                const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndTriX *ind_obj=new CIndTriX(symbol,timeframe,ma_period,applied_price);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create TriX indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add Larry Williams' Percent Range to the collection              |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewWPR(const string symbol,const ENUM_TIMEFRAMES timeframe,const int calc_period=14)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndWPR *ind_obj=new CIndWPR(symbol,timeframe,calc_period);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create WPR indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add Variable Index Dynamic Average to the collection             |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewVIDyA(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                  const int cmo_period=9,
                                  const int ema_period=12,
                                  const int ma_shift=0,
                                  const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndVIDyA *ind_obj=new CIndVIDyA(symbol,timeframe,cmo_period,ema_period,ma_shift,applied_price);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create VIDyA indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add the Volumes indicator to the collection                      |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewVolumes(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK)
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndVolumes *ind_obj=new CIndVolumes(symbol,timeframe,applied_volume);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create Volumes indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Add a custom indicator to the collection                         |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewCustom(const string symbol,const ENUM_TIMEFRAMES timeframe,const string path,const string name,const uint buffers,const MqlParam &param[])
  {
//--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE
   CIndCustom *ind_obj=new CIndCustom(symbol,timeframe,path,name,buffers,param);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create %s custom indicator object",__FUNCTION__,name);
      return INVALID_HANDLE;
     }
//--- Return the result of adding the created indicator object to the collection list
   return this.AddNewIndicator(ind_obj,__FUNCTION__);
  }

指标集合类的所有代码都已准备就绪。每个方法都提供了描述其逻辑的注释,以便它们易于理解。


测试

要测试多指标的类及其集合,让我们创建一个简单的指标。该指标将从几个移动平均线中选出一个。这些测试的目的是检查具有指标的类的操作,这些指标从主图表窗口中的一个缓冲区中绘制一条线。此目的最好使用来自客户端终端中的趋势指标集合的移动平均值来实现。在接下来的文章中,我们将创建模板,用于快速创建和使用终端中提供的标准集中的任何指标+使用自定义指标。渐渐地,我们将完全完成今天创建的类,以便正确使用任何类型的指标。

测试指标将在图表上绘制创建的多个指标的线条。相关数据将显示在仪表板上。该仪表板的创建在本系列的第一篇文章中进行了描述。

我们将在一个指标中创建两个相同的指标。其中一个将使用当前图表数据进行计算,第二个将使用在设置中选择的交易品种/周期的数据进行计算。这样,我们将始终看到当前图表上的移动平均线和使用不同图表周期的数据创建的同样的移动平均线。可以从另一个图表中选择一个交易品种,但线条将与价格水平不匹配。

创建一个名为TestMSTFMovingAverages.mq5的新指标文件:


选择OnTimer和OnChartEvent处理函数:

在主图表窗口中选择要绘制为线条的两个缓冲区:

您可以使用任何缓冲区名称,因为它们将在代码中重命名。点击“完成”,获得一个指标模板:

//+------------------------------------------------------------------+
//|                                       TestMSTFMovingAverages.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   2
//--- plot MA1
#property indicator_label1  "MA1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot MA2
#property indicator_label2  "MA2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- indicator buffers
double         MA1Buffer[];
double         MA2Buffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,MA1Buffer,INDICATOR_DATA);
   SetIndexBuffer(1,MA2Buffer,INDICATOR_DATA);
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   
//--- return value of prev_calculated for the next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---
   
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
   
  }

在全局区域中,添加一个枚举以选择移动平均线的类型连接多交易品种多周期指标类文件和仪表板类文件,并且声明输入变量
重命名缓冲区
以提供更有意义的表示,并声明全局指标变量

//+------------------------------------------------------------------+
//|                                       TestMSTFMovingAverages.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   2
//--- enums
enum ENUM_USED_MA
  {
   USED_MA_AMA    =  IND_AMA,    // Adaptive Moving Average
   USED_MA_DEMA   =  IND_DEMA,   // Double Exponential Moving Average
   USED_MA_FRAMA  =  IND_FRAMA,  // Fractal Adaptive Moving Average
   USED_MA_MA     =  IND_MA,     // Moving Average
   USED_MA_TEMA   =  IND_TEMA,   // Triple Exponential Moving Average
   USED_MA_VIDYA  =  IND_VIDYA,  // Variable Index Dynamic Average
  };
//--- plot MA1
#property indicator_label1  "MA1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot MA2
#property indicator_label2  "MA2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input ENUM_USED_MA         InpIndicator   =  USED_MA_MA;       /* Used MA        */ // Type of moving average to use
input string               InpSymbol      =  NULL;             /* Symbol         */ // Moving average symbol
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe      */ // Moving average timeframe
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;      /* Applied Price  */ // Price used for calculations
input ENUM_MA_METHOD       InpMethod      =  MODE_SMA;         /* MA Method      */ // Moving Average calculation method
input int                  InpShift       =  0;                /* MA Shift       */ // Moving average shift
input bool                 InpAsSeries    =  true;             /* As Series flag */ // Timeseries flag of indicator buffer arrays

//--- indicator buffers
double         BufferMA1[];
double         BufferMA2[];
//--- global variables
int handle_ma1;
int handle_ma2;
CMSTFIndicators indicators;      // An instance of the indicator collection object
//--- variables for the panel
CDashboard *panel=NULL;          // Pointer to the panel object
int         mouse_bar_index;     // Index of the bar the data is taken from

OnInit()处理函数中,输入计时器的创建,分配绘图缓冲区,为所选指标创建句柄,并创建仪表板:

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Set a timer with an interval of 1 second
   EventSetTimer(1);
//--- Assign the BufferMA1 and BufferMA2 arrays to the plot buffers 0 and 1, respectively
   SetIndexBuffer(0,BufferMA1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferMA2,INDICATOR_DATA);
//--- sets indicator shift
   //PlotIndexSetInteger(0,PLOT_SHIFT,InpShift);   // analog in line 116
   //PlotIndexSetInteger(1,PLOT_SHIFT,InpShift);   // analog in line 117
//--- Set the timeseries flags for the indicator buffer arrays (for testing, to see that there is no difference)
   ArraySetAsSeries(BufferMA1,InpAsSeries);
   ArraySetAsSeries(BufferMA2,InpAsSeries);
   
//--- For different indicators, the dashboard width will be individual (due to the number of parameters in the description)
   int width=0;
//--- According on the indicator selected in the settings, create two indicators of the same type
//--- The first one is calculated on the current chart symbol/period, the second - on those specified in the settings
   switch(InpIndicator)
     {
      case USED_MA_AMA     :
         handle_ma1=indicators.AddNewAMA(NULL,PERIOD_CURRENT,9,2,30,InpShift);
         handle_ma2=indicators.AddNewAMA(InpSymbol,InpTimeframe,9,2,30,InpShift);
         width=269;
        break;
      case USED_MA_DEMA    :
         handle_ma1=indicators.AddNewDEMA(NULL,PERIOD_CURRENT,14,InpShift,InpPrice);
         handle_ma2=indicators.AddNewDEMA(InpSymbol,InpTimeframe,14,InpShift,InpPrice);
         width=255;
        break;
      case USED_MA_FRAMA   :
         handle_ma1=indicators.AddNewFrAMA(NULL,PERIOD_CURRENT,14,InpShift,InpPrice);
         handle_ma2=indicators.AddNewFrAMA(InpSymbol,InpTimeframe,14,InpShift,InpPrice);
         width=259;
        break;
      case USED_MA_TEMA    :
         handle_ma1=indicators.AddNewTEMA(NULL,PERIOD_CURRENT,14,InpShift,InpPrice);
         handle_ma2=indicators.AddNewTEMA(InpSymbol,InpTimeframe,14,InpShift,InpPrice);
         width=253;
        break;
      case USED_MA_VIDYA   :
         handle_ma1=indicators.AddNewVIDyA(NULL,PERIOD_CURRENT,9,12,InpShift,InpPrice);
         handle_ma2=indicators.AddNewVIDyA(InpSymbol,InpTimeframe,9,12,InpShift,InpPrice);
         width=267;
        break;
      default:
         handle_ma1=indicators.AddNewMA(NULL,PERIOD_CURRENT,10,InpShift,InpMethod,InpPrice);
         handle_ma2=indicators.AddNewMA(InpSymbol,InpTimeframe,10,InpShift,InpMethod,InpPrice);
         width=231;
        break;
     }
//--- If failed to create indicator handles, return initialization error
   if(handle_ma1==INVALID_HANDLE || handle_ma2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Set descriptions for indicator lines from buffer descriptions of calculation part of created indicators
   indicators.SetPlotLabelFromBuffer(0,handle_ma1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_ma2,0);
//--- Set "empty" values for calculation part buffers of the created indicators
   indicators.SetBufferInitValue(handle_ma1,0,EMPTY_VALUE);
   indicators.SetBufferInitValue(handle_ma2,0,EMPTY_VALUE);
//--- Set shifts for indicator lines
   indicators.SetPlotShift(0,InpShift);
   indicators.SetPlotShift(1,InpShift);
      
//--- Dashboard
//--- Create the panel
   panel=new CDashboard(1,20,20,width,264);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Set font parameters
   panel.SetFontParams("Calibri",9);
//--- Display the panel with the "Symbol, Timeframe description" header text
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Create a table with ID 0 to display bar data in it
   panel.CreateNewTable(0);
//--- Draw a table with ID 0 on the dashboard background
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Create a table with ID 1 to display the data of indicator 1
   panel.CreateNewTable(1);
//--- Get the Y2 table coordinate with ID 0 and
//--- set the Y1 coordinate for the table with ID 1
   int y1=panel.TableY2(0)+22;
//--- Draw a table with ID 1 on the dashboard background
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Create a table with ID 2 to display the data of indicator 2
   panel.CreateNewTable(2);
//--- Get the Y2 coordinate of the table with ID 1 and
//--- set the Y1 coordinate for the table with ID 2
   int y2=panel.TableY2(1)+3;
//--- Draw a table with ID 2 on the background of the dashboard
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Initialize the variable with the index of the mouse cursor bar
   mouse_bar_index=0;
//--- Display the data of the current bar on the dashboard
   DrawData(mouse_bar_index,TimeCurrent());

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }

添加OnDeinit()处理函数:

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Delete the timer
   EventKillTimer();
//--- If the panel object exists, delete it
   if(panel!=NULL)
      delete panel;
//--- Delete all comments
   Comment("");
  }

OnCalculate()处理函数中,调用所有多交易品种多周期指标的计算如果计算失败,请退出处理函数并返回零以在下一个分时重新计算指标。
计算成功后,指标对象的数组缓冲区中的数据已经存在,并且可以显示在仪表板中。在仪表板上显示数据后,将数据从计算的缓冲区输出到指标的绘图缓冲区

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- Number of bars for calculation
   int limit=rates_total-prev_calculated;
//--- If limit > 1, then this is the first calculation or change in the history
   if(limit>1)
     {
      //--- specify all the available history for calculation
      limit=rates_total-1;
      /*
      // If the indicator has any buffers that display other calculations (not multi-indicators),
      // initialize them here with the "empty" value set for these buffers
      */
     }
//--- Calculate all created multi-symbol multi-period indicators
   if(!indicators.Calculate())
      return 0;

//--- Display the bar data under cursor (or current bar if cursor is outside the chart) on the dashboard
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- From buffers of calculated indicators, output data to indicator buffers
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_ma1,0,limit,BufferMA1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_ma2,0,limit,BufferMA2))
      return 0;

//--- return value of prev_calculated for the next call
   return(rates_total);
  }

在指标计时器中,调用指标集合对象的计时器:

//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Call the indicator collection timer
   indicators.OnTimer();
  }

OnChartEvent()处理函数中,调用仪表板对象事件处理函数并处理光标移动以确定其所在的柱:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Handling the panel
//--- Call the panel event handler
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- If the cursor moves or a click is made on the chart
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Declare the variables to record time and price coordinates in them
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- If the cursor coordinates are converted to date and time
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- write the bar index where the cursor is located to a global variable
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Display the bar data under the cursor on the panel 
         DrawData(mouse_bar_index,time);
        }
     }

//--- If we received a custom event, display the appropriate message in the journal
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Here we can implement handling a click on the close button on the panel
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

在仪表板上显示多指标数据的函数:

//+------------------------------------------------------------------+
//| Display data from the specified timeseries index to dashboard    |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Declare the variables to receive data in them
   MqlRates rates[1];

//--- Exit if unable to get the bar data by the specified index
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Set font parameters for bar and indicator data headers
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Set font parameters for bar and indicator data
   panel.SetFontParams(name,9);

//--- Display the data of the specified bar in table 0 on the panel
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Output the data of indicator 1 from the specified bar into table 1
   panel.DrawText(indicators.Title(handle_ma1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_ma1,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_ma1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Display a description of the indicator 1 line state
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_ma1,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Output the data of indicator 2 from the specified bar into table 2
   panel.DrawText(indicators.Title(handle_ma2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_ma2,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_ma2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
   
//--- Display a description of the indicator 2 line state
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_ma2,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
   
//--- Display description of relationship between indicator 1 line relative to indicator 2 line
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_ma2,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_ma1,0,index,value2,value21);
   string ma1=indicators.Name(handle_ma1);
   string ma2=indicators.Name(handle_ma2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,110);
   
//--- Redraw the chart to immediately display all changes on the panel
   ChartRedraw(ChartID());
  }

正如您所看到的,我们可以通过简单地调用指标集合类的 Calculate()方法来计算多交易品种、多周期的指标。计算成功后,所有数据都已可用。这些数据可以从 EA 交易中访问和处理。在指标中,计算成功后,可以使用指标集合类的 DataToBuffer()方法将数据以线的形式显示在图表上。这就是计算和在图表上显示多个指标所需的全部内容。

编译测试指标后,我们将在周期为M1的图表上启动它,在设置中我们将选择指标M5的当前交易品种和计算周期。在这种情况下,将创建在指标设置中选择的两个移动平均线。其中一个将使用当前图表数据进行计算,第二个将基于五分钟图表周期的数据。通过切换图表时间框架,您可以看到M1上画了两条线:一条线对应M1上计算的移动平均线,第二条线对应M5上计算的移动平均线。如果将图表切换到M5,则只会创建一个指标,因为第二个指标与第一个指标相同,并且不会创建。如果将图表切换到M15,则将为M15计算一个指标,为M5计算第二个指标,并且该指标也将显示在图表上。


正如您所看到的,声明的功能是有效的,我们可以在主图表窗口中看到指标。可以使用一个缓冲区创建多个指标。

附件中提供了所有类的文件和测试指标。


结论

今天,我们已经创建了快速创建多交易品种多周期指标、接收指标中的数据以及在主图表窗口中绘制计算指标的功能。在下面的文章中,我们将创建用于创建其他多交易品种、多周期标准指标的模板,这些指标在主图表窗口中绘制数据,而不仅仅是使用一个缓冲区。因此,我们将获得一个方便的工具,可以快速将任何指标转换为多交易品种、多周期版本。随着我们将创建其他标准和自定义指标,多指标类可能会得到进一步发展。


本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/13578

附加的文件 |
Dashboard.mqh (217.85 KB)
IndMSTF.mqh (473.51 KB)
神经网络变得轻松(第五十一部分):行为-指引的扮演者-评论者(BAC) 神经网络变得轻松(第五十一部分):行为-指引的扮演者-评论者(BAC)
最后两篇文章研究了软性扮演者-评论者算法,该算法将熵正则化整合到奖励函数当中。这种方式在环境探索和模型开发之间取得平衡,但它仅适用于随机模型。本文提出了一种替代方式,能适用于随机模型和确定性模型两者。
开发回放系统 — 市场模拟(第 22 部分):外汇(III) 开发回放系统 — 市场模拟(第 22 部分):外汇(III)
虽然这是关于这个主题的第三篇文章,但我必须为那些还不了解股票市场和外汇市场之间区别的人解释一下:最大的区别在于,在外汇中没有、或者更确切地说,我们得不到交易过程中有关一些实际发生关键处的信息。
在MQL5中置换价格柱 在MQL5中置换价格柱
在这篇文章中,我们提出了一种置换价格柱的算法,并详细说明了如何使用置换测试来识别策略性能被编造来欺骗 EA 交易的潜在买家的情况。
MQL5 中的范畴论 (第 14 部分):线性序函子 MQL5 中的范畴论 (第 14 部分):线性序函子
本文是更广泛关于以 MQL5 实现范畴论系列的一部分,深入探讨了函子(Functors)。我们实验了如何将线性序映射到集合,这要归功于函子;通过研究两组数据,典型情况下会忽略其间的任何联系。