English Русский 中文 Deutsch 日本語
preview
Preparação de indicadores com vários símbolos/períodos

Preparação de indicadores com vários símbolos/períodos

MetaTrader 5Indicadores | 26 março 2024, 13:27
194 0
Artyom Trishkin
Artyom Trishkin

Conteúdo


Introdução

Hoje começaremos o tema de criar indicadores multissímbolo e multiperíodo, para dar continuação ao tema da criação de templates de indicadores técnicos em EAs e indicadores, iniciado no artigo sobre a criação de um painel de informações, e que se desenvolve ao longo de mais três artigos sobre osciladores, indicadores de volume e Bill Williams, assim como indicadores de tendência. Esses indicadores rodam no gráfico atual, mas são calculados com base nos dados de outros símbolos e/ou diferentes timeframes. Criaremos uma só classe base para o multi-indicador e um conjunto de classes por tipos para todos os indicadores padrão. Quanto ao indicador personalizado, também elaboraremos uma classe, o que nos dará a possibilidade de "transformar" qualquer indicador em multissímbolo e multiperíodo. Já quanto às classes de indicadores, criaremos uma só classe coleção geral, na qual colocaremos os indicadores gerados no programa, o que facilitará acessar qualquer um dos indicadores criados através dos métodos da coleção para recuperar seus dados. Em suma, o objetivo será tornar fácil tanto a criação de multi-indicadores como o trabalho com seus dados.


Princípios básicos

Para entender corretamente a lógica de trabalho com indicadores, tentaremos desvendar um pouco como tudo isso é estruturado. Bem, um indicador tem duas partes: a de cálculo e a de desenho. Cada uma dessas partes não sabe nada sobre a outra. Agora, ao criar um indicador, o subsistema do terminal verifica a presença do indicador no gráfico, conferindo o mesmo nome e parâmetros. Lembre, quando o indicador em questão já está anexado ao gráfico, ou criado para ele programaticamente, é usado o handle do indicador existente, em vez de criar um novo. Aqui, para a plotagem, a parte de desenho do indicador recebe dados da parte de cálculo mediante seu handle. Pode acontecer que várias partes de desenho acessarem uma só parte de cálculo.

No buffer da parte de cálculo, os dados do indicador calculado são armazenados em um array com os dados organizados do presente para o passado. Os dados no índice 0 do array-buffer correspondem aos dados atuais do gráfico:


Cada célula do array armazena os dados de uma barra, dados esses que correspondem à barra da série temporal do símbolo/período com o qual o indicador foi calculado. Desse modo, para obter dados do buffer da parte de cálculo do indicador e exibi-los no gráfico de outro símbolo/timeframe, é necessário calcular o número da barra no gráfico, correspondente ao tempo da barra no array-buffer da parte de cálculo. Os dados obtidos são registrados no buffer da parte de desenho do indicador de tal forma que todas as barras do gráfico atual, que coincidem com o tempo de abertura da barra no buffer da parte de cálculo, sejam inseridas nas células correspondentes do buffer de desenho.

Assi, por exemplo, uma barra no período gráfico de cinco minutos corresponde a cinco barras do gráfico de um minuto. Todas essas cinco barras do gráfico de um minuto devem ser preenchidas com o valor da barra do período gráfico de cinco minutos correspondente. Um algoritmo parecido é usado ao plotar dados de períodos menores no gráfico de um período maior. Neste caso, todas as barras das células do buffer da parte de cálculo, correspondentes ao tempo da barra do timeframe maior, são plotadas em uma só barra do buffer de desenho.

Aqui, naturalmente, haverá uma certa concessão nas leituras, pois no final apenas os dados da última barra do timeframe menor, que ocorrem no mesmo tempo da respectiva barra do timeframe maior, serão desenhados nesta barra. Tudo depende da recuperação de dados a partir do buffer da parte de cálculo do timeframe menor, em outras palavras, são os dados obtidos por último que serão desenhados na barra do gráfico do timeframe maior.

A função CopyBuffer() se destina a recuperar dados a partir do buffer do indicador calculado:

Obtém no array buffer os dados do buffer especificado do indicador especificado, na quantidade especificada.

A contagem dos elementos dos dados copiados (buffer de indicador com índice buffer_num) a partir da posição inicial é feita do presente para o passado, ou seja, a posição inicial igual a 0 representa a barra atual (valor do indicador para a barra atual).

Ao copiar um número desconhecido de dados como um array receptor buffer[], é recomendável usar um array dinâmico, pois a função CopyBuffer() tenta alocar o tamanho do array receptor para se adequar ao tamanho dos dados copiados. Se o array receptor buffer[] atuar como um buffer de indicador (array, previamente designado para armazenar valores do indicador pela função SetIndexBuffer()), então é permitida a cópia parcial.

Se for necessário realizar a cópia parcial de valores do indicador em outro array (não um buffer de indicador), então, é necessário usar um array intermediário, no qual é copiado o número necessário de dados. E, a partir desse array intermediário, deve-se realizar a cópia elemento por elemento do número necessário de valores nos locais apropriados do array receptor.

Se for necessário copiar um número conhecido de dados, é melhor fazer isso em um buffer estático alocado, para evitar o realocamento excessivo de memória.

Não importa qual propriedade o array receptor tenha, seja ela as_series=true ou as_series=false, os dados serão copiados de tal forma que o elemento mais antigo estará no início da memória física alocada para o array. Existem 3 variantes da função.

Acesso pela posição inicial e quantidade de elementos necessários

int  CopyBuffer(
   int       indicator_handle,     // handle индикатора
   int       buffer_num,           // номер буфера индикатора
   int       start_pos,            // откуда начнем 
   int       count,                // сколько копируем
   double    buffer[]              // массив, куда будут скопированы данные
   );

Acesso pela data inicial e quantidade de elementos necessários

int  CopyBuffer(
   int       indicator_handle,     // handle индикатора
   int       buffer_num,           // номер буфера индикатора
   datetime  start_time,           // с какой даты
   int       count,                // сколько копируем
   double    buffer[]              // массив, куда будут скопированы данные
   );

Acesso pelas datas inicial e final do intervalo de tempo necessário

int  CopyBuffer(
   int       indicator_handle,     // handle индикатора
   int       buffer_num,           // номер буфера индикатора
   datetime  start_time,           // с какой даты
   datetime  stop_time,            // по какую дату
   double    buffer[]              // массив, куда будут скопированы данные
   );

Parâmetros

indicator_handle

[in]  Handle do indicador, obtido pela função indicadora correspondente.

buffer_num

[in]  Número do buffer indicador.

start_pos

[in]  Número do primeiro elemento a ser copiado.

count

[in]  Quantidade de elementos a serem copiados.

start_time

[in]  Hora da barra, correspondente ao primeiro elemento.

stop_time

[in]  Hora da barra, correspondente ao último elemento.

buffer[]

[out]  Array do tipo double.

Valor de retorno

Quantidade de elementos do array copiados ou -1 em caso de erro.

Nota

Ao solicitar dados do indicador, se as séries temporais solicitadas ainda não estiverem construídas ou precisarem ser carregadas do servidor, a função imediatamente retornará -1, mas, ao mesmo tempo, o processo de carregamento/construção será iniciado.

Ao solicitar dados de um EA ou script, será iniciado o carregamento do servidor, caso esses dados não estejam disponíveis localmente no terminal, ou começará a construção da série temporal necessária, se os dados puderem ser construídos a partir do histórico local, mas ainda não estiverem prontos. A função retornará a quantidade de dados que estiverem prontos no momento da expiração do tempo de espera.

Usaremos a primeira variante da função, isto é, o acesso pela posição inicial (índice do laço) e quantidade de elementos necessários.

A estrutura dos objetos será a seguinte:

  1. Classe base para indicadores multissímbolo/multiperíodo que contém o funcional principal, comum a todos os indicadores;
  2. Classes derivadas do objeto base que descrevem cada indicador por seu tipo;
  3. Classe de coleção de indicadores que permite criar quaisquer indicadores e adicioná-los à coleção. A classe fornecerá ao usuário todo o conjunto de ferramentas para criar indicadores e recuperar dados deles.

Ao trabalhar com dados não pertencentes ao gráfico atual, para evitar o "desalocamento" da série temporal, é necessário acessar essa série temporal pelo menos uma vez a cada dois minutos. Nesse caso, ocorrerá a "retenção" da série temporal, o que acelerará o acesso a ela (não será necessário esperar pela sincronização dos dados a cada vez). No construtor da classe, faremos o primeiro acesso à série temporal na qual o indicador é construído, o que permitirá iniciar seu carregamento (se não houver acesso a ela localmente). E, em seguida, a cada 90 segundos (um minuto e meio) no timer da classe base, acessaremos a série temporal para mantê-la.


Calculo eficiente do indicador

Para a exibição do indicador, é necessário um cálculo eficiente de modo que, no primeiro lançamento, o indicador possa ser calculado com base em todos os dados históricos, enquanto nos lançamentos subsequentes apenas uma ou duas barras sejam suficientes.

No manipulador OnCalculate() existem variáveis constantes predefinidas que armazenam o tamanho dos dados de entrada (série temporal ou array) e o número de dados calculados na chamada anterior do OnCalculate():

Cálculo baseado em array de dados

int  OnCalculate(
   const int        rates_total,       // размер массива price[]
   const int        prev_calculated,   // количество обработанных баров на предыдущем вызове
   const int        begin,             // номер индекса в массиве price[], с которого начинаются значимые данные
   const double&    price[]            // массив значений для расчета
   );

Cálculos baseados nas séries temporais do timeframe atual

int  OnCalculate(
   const int        rates_total,       // размер входных таймсерий
   const int        prev_calculated,   // количество обработанных баров на предыдущем вызове
   const datetime&  time{},            // массив Time
   const double&    open[],            // массив Open
   const double&    high[],            // массив High
   const double&    low[],             // массив Low
   const double&    close[],           // массив Close
   const long&      tick_volume[],     // массив Tick Volume
   const long&      volume[],          // массив Real Volume
   const int&       spread[]           // массив Spread
   );

A presença desses dados permite fazer um cálculo rápido e eficiente do indicador. Por exemplo, um desses cálculos poderia ser a determinação do valor de limit:

//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      //--- Если в индикаторе есть какие-либо буферы, то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
     }

Na primeira execução do indicador, temos o tamanho da série temporal (rates_total) e a quantidade de dados calculados na chamada anterior (prev_calculated). O valor das barras calculadas anteriormente na primeira execução é zero, portanto o indicador ainda não foi calculado. Assim, o valor de limit será maior que 1 (será igual ao número de barras disponíveis menos zero). Nesse caso, definimos o valor de limit como igual a rates_total-1, o que representa todo o histórico disponível para cálculo. Assim sendo, é necessário primeiramente apagar todos os dados previamente plotados nos buffers do indicador, por exemplo.

ArrayInitialize(Buffer,EMPTY_VALUE);

Depois disso, no laço principal, será calculado todo o histórico, porque o laço é construído a partir do valor de limit até zero:

   for(int i=limit;i>=0;i--)
     {
      // Расчёт индикатора на каждом баре цикла (i)
     }  

Deve-se notar que, com esse método de cálculo, todos os arrays utilizados nos cálculos e o próprio buffer do indicador devem ter a flag de indexação como para a série temporal:

ArraySetAsSeries(array,true);

Se o limit calculado for igual a 1, significa a abertura de uma nova barra no gráfico: o indicador calculará a primeira e a barra zero da série temporal, com o laço indo de 1 a 0, incluindo ambos.

Quando o limit calculado é igual a 0, isso indica que estamos trabalhando no tick atual: o indicador calculará apenas a barra zero da série temporal, com o laço indo de 0 a 0, incluindo este último.

Se um cálculo é necessário a partir da barra zero indo para trás nos dados históricos (para evitar inverter os arrays de séries temporais e buffers), então a estrutura do laço será invertida:

   int i=fmax(0, prev_calculated-1);
   for(;i<rates_total;i++)
     {
      // Расчёт индикатора на каждом баре цикла (i)
     }   

Assim, já é fácil fazer um cálculo eficiente no gráfico atual. Mas e se forem necessários os dados de um gráfico diferente? Quando copiar todo o array de dados da parte de cálculo e quando copiar apenas uma ou duas últimas barras?

Aqui é onde as funções Bars() e BarsCalculated() podem ajudar. Estas são análogas às variáveis constantes predefinidas do indicador rates_total e prev_calculated, mas retornam o número de barras para o símbolo/período especificado e a quantidade de dados calculados pelo indicador. Como o indicador é construído sobre o símbolo/período especificado, então a quantidade de dados calculados se refere a esse símbolo/período. O indicador é obtido pelo seu handle.

Com base no fato de que podemos calcular para qualquer símbolo/período quantas barras precisam ser copiadas (para evitar copiar o array inteiro a cada tick), faremos exatamente a mesma estrutura na classe base do indicador, assim como para o símbolo/período atual.

limit=rates_total-prev_calculated;

Apenas as variáveis limit, rates_total e prev_calculated serão membros privados da classe, e seus valores serão obtidos das funções Bars() e BarsCalculated(). Em cada tick, será calculado o limit, e, se for zero, apenas os dados das duas últimas barras (a barra atual e o anterior) serão copiados. Se o limit for igual a 1, isso indica a abertura de uma nova barra no símbolo/período do indicador, e é necessário aumentar o array em 1, e então copiar os dados a partir do buffer do indicador — também apenas dois. Quando o limit é maior que um, o array inteiro do buffer de cálculo do indicador é copiado para o array-buffer receptor da classe, pois se assume que isso representa ou o primeiro lançamento ou uma mudança no histórico.

Essa lógica se aplica ao pregão, quando os ticks estão aparecendo.

Um dia de folga, entretanto, requer uma abordagem diferente. Este é um caso isolado. Aqui não há ticks, a função Bars() muitas vezes retorna zero, e o número de barras calculadas pelo indicador não é registrado, sendo também zero. O indicador, se houver algum erro nos dados de origem para o cálculo, deve retornar zero, para, assim, esperar pela atualização dos dados até o próximo tick e recalcular. Mas no dia de folga, não há tick além do primeiro lançamento.

No primeiro lançamento, o indicador primeiro limpará os arrays de buffer, depois será calculado. Mas o cálculo pode falhar por insuficiência de dados para o cálculo, ou simplesmente porque prev_calculated retorna um valor zero. E o indicador sai de OnCalculate(), retornando zero novamente. Por isso, se você atualizar o gráfico com o botão direito do mouse, o que é essencialmente uma emulação de tick, então o indicador verá novamente que foi calculado com erro e inicializará os buffers novamente, pensando que é o primeiro lançamento. E assim por diante, apagando os dados recém-plotados no gráfico dos buffers de desenho... Mas há uma saída.

Se no primeiro lançamento o cálculo do indicador não funcionou imediatamente, você pode esperar o próximo tick por 20 a 30 segundos e emular um tick programaticamente. Isso pode ser feito usando a função ChartSetSymbolPeriod(). Se chamada, especificando o símbolo/período atual do gráfico, isso causará o recálculo dos indicadores anexados ao gráfico. Dessa forma, é possível calcular o indicador no gráfico mesmo na ausência de ticks.

Basta esperar 20 segundos para carregar o histórico necessário do símbolo/período do indicador e calculá-lo. Mas precisamos de uma flag de que o indicador já foi calculado (já que prev_calculated retorna zero), e se não marcarmos a flag de sucesso do cálculo, o indicador apagará seus buffers novamente. Por isso, para entender que o indicador foi calculado com sucesso, basta ver que o número de barras calculadas para o objeto indicador é igual ao número de barras para seu símbolo/período. Se Bars() retorna zero, podemos descobrir de outra maneira o número disponível de barras do símbolo/período necessário (não esquecemos que estamos falando de indicadores multissímbolo e multiperíodo, que são calculados em outro programa — um EA ou indicador, lançado no gráfico atual). Na função SeriesInfoInteger() podemos obter a data de início do histórico disponível por símbolo/período e a data de término:

SeriesInfoInteger(symbol,period,SERIES_FIRSTDATE);      // Самая первая дата по символу-периоду на данный момент
SeriesInfoInteger(symbol,period,SERIES_LASTBAR_DATE);   // Время открытия последнего бара по символу-периоду

A partir dessas duas datas e do período do gráfico da série temporal, podemos facilmente calcular o número de barras disponíveis, mesmo se Bars(symbol,period) ou SeriesInfoInteger(symbol,period,SERIES_BARS_COUNT) retornarem zero.

Após todos os dados serem recebidos e o indicador ser corretamente calculado, a flag de sucesso do cálculo é definida. Essa flag é verificada quando limit > 1 (nesse caso, é necessário inicializar os arrays de buffer do indicador com um "valor vazio"). Na primeira execução, a flag de sucesso é resetada — os arrays são inicializados e é feita uma tentativa de calcular o indicador no símbolo/período do gráfico atual. Se o cálculo falhar, espera-se 20 segundos no timer do próximo tick.

Se for um dia de folga, não haverá tick, e após 20 segundos é enviada uma ordem para ajustar o gráfico ao símbolo e período atuais — emulação de tick. Na reinicialização do indicador no timer (após 20 segundos), os dados já devem ter sido carregados e o indicador deve ser calculado sem erros. Neste ponto, é definida a flag de sucesso do cálculo. Na próxima entrada no timer, esta flag é verificada e, se estiver definida, o tick não será emulado. Existem um total de três tentativas para desenhar os buffers do indicador. Após as três tentativas, a flag de sucesso é forçosamente definida, e as tentativas de emulação de tick são encerradas. Se o indicador não conseguir ser calculado após três ticks emulados, então só resta manualmente: ou (botão direito do mouse --> Atualizar), ou alternar o timeframe do gráfico para frente e para trás, para reiniciar todo o processo de emulação de ticks com o carregamento de dados.

Essa é a teoria. Vamos passar para a prática, a criação de classes de multi-indicadores.


Classe base do indicador MSTF

Na pasta do terminal \MQL5\Include\IndMSTF, vamos criar um novo arquivo IndMSTF.mqh da classe CIndMSTF. A classe deve ser herdada do objeto base da Biblioteca Padrão CObject. O arquivo do objeto base deve ser incluído no arquivo da nova classe criada:

//+------------------------------------------------------------------+
//|                                                      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>
//+------------------------------------------------------------------+
//| Базовый класс мультисимвольного мультипериодного индикатора      |
//+------------------------------------------------------------------+
class CIndMSTF : public CObject
  {
private:

protected:

public:
//--- Конструктор/деструктор
                     CIndMSTF();
                    ~CIndMSTF();
  };

Antes de adicionarmos métodos nas diferentes seções da classe, vamos adicionar algumas macros, enumerações e a estrutura do buffer do indicador.

Precisaremos de um timer de segundos, para monitorar dois períodos de tempo:

  • 90 segundos, após os quais acessaremos séries temporais de indicadores calculados não fora do símbolo/período atual,
  • 20 segundos, após os quais emularemos um tick para desenhar o indicador nos dias de folga.
Vamos inserir as macros para definir os tempos de espera desses dois períodos:
//--- includes
#include <Object.mqh>
//--- defines
#define TIMER_COUNT_1   (90)  // Размер таймера 1. Не должен быть более двух минут (120)
#define TIMER_COUNT_2   (20)  // Размер таймера 2. Слишком малые значения быстро запускают эмуляцию тика, что не желательно при активном рынке

Diferentes indicadores padrão no terminal do cliente pertencem a diferentes categorias. Para podermos ordenar os indicadores criados por categorias ou criar listas de indicadores pertencentes a uma determinada categoria, vamos escrever uma enumeração das diferentes categorias de indicadores:

//--- defines
#define TIMER_COUNT_1   (90)  // Размер таймера 1. Не должен быть более двух минут (120)
#define TIMER_COUNT_2   (20)  // Размер таймера 2. Слишком малые значения быстро запускают эмуляцию тика, что не желательно при активном рынке
//--- enums
enum ENUM_IND_CATEGORY        // Категории индикаторов
  {
   IND_CATEGORY_NONE,         // Не задана
   IND_CATEGORY_TREND,        // Трендовые
   IND_CATEGORY_OSCILLATOR,   // Осцилляторы
   IND_CATEGORY_VOLUME,       // Объемы
   IND_CATEGORY_WILLIAMS,     // Билла Вильямса
   IND_CATEGORY_CUSTOM,       // Пользовательские
  };


Sobre classificação, pesquisa e filtragem

Cada objeto indicador terá propriedades pelas quais será possível encontrar o indicador necessário. Para entender que os indicadores são semelhantes entre si, é necessário comparar suas propriedades-chave: símbolo, período gráfico e os valores de todos os parâmetros de entrada. Se pelo menos um valor das propriedades comparadas for diferente, os indicadores não são idênticos. Se os indicadores forem iguais, não será criado um novo, mas será retornado um ponteiro para o já criado com os mesmos parâmetros. Isso quanto à coleção de indicadores. Quanto às propriedades, precisamos criar uma enumeração na qual serão escritas as constantes de algumas propriedades que podem ser atribuídas com sucesso ao indicador criado, e depois encontrá-lo com base nelas:

enum ENUM_COMPARE_MODE        // Режим сравнения
  {
   // По умолчанию режим сравнения имеет нулевое значение, что производит сравнение по всем свойствам
   COMPARE_MODE_HANDLE=1,     // Сравнение по хэндлу
   COMPARE_MODE_SYMBOL,       // Сравнение по символу
   COMPARE_MODE_TIMEFRAME,    // Сравнение по периоду графика
   COMPARE_MODE_ID,           // Сравнение по идентификатору
   COMPARE_MODE_DESCRIPTION,  // Сравнение по пользовательскому описанию
   COMPARE_MODE_CATEGORY,     // Сравнение по категории
  };

Cada indicador criado com sucesso possui um handle, e podemos acessá-lo através desse handle. Esse é um número único atribuído à parte de cálculo do indicador. Os valores dos handles dos indicadores criados começam em 10 e aumentam em 1 para cada indicador subsequente.

As demais propriedades não são únicas e podem ser comuns a diferentes indicadores. A busca por elas é apenas por conveniência. Por exemplo, você pode definir uma descrição única para o indicador e, em seguida, acessá-lo por essa descrição.

Sobre as descrições dos estados das linhas do indicador, já falamos em artigos anteriores sobre indicadores. Aqui também usaremos essa enumeração:

enum ENUM_LINE_STATE          // Состояние линии индикатора
  {
   LINE_STATE_NONE,           // Неопределённое состояние
   LINE_STATE_UP,             // Направление вверх
   LINE_STATE_DOWN,           // Направление вниз
   LINE_STATE_TURN_UP,        // Разворот вверх
   LINE_STATE_TURN_DOWN,      // Разворот вниз
   LINE_STATE_STOP_UP,        // Остановка направления вверх
   LINE_STATE_STOP_DOWN,      // Остановка направления вниз
   LINE_STATE_ABOVE,          // Над значением
   LINE_STATE_BELOW,          // Под значением
   LINE_STATE_CROSS_UP,       // Пересечение значения вверх
   LINE_STATE_CROSS_DOWN,     // Пересечение значения вниз
   LINE_STATE_TOUCH_BELOW,    // Касание значения снизу
   LINE_STATE_TOUCH_ABOVE,    // Касание значения сверху
   LINE_STATE_EQUALS,         // Равно значению
  };

Você pode ler mais sobre a enumeração no artigo sobre indicadores osciladores na seção de parâmetros do indicador ATR.

Cada indicador sinalizará o resultado de seu cálculo por meio de um código de erro:

enum ENUM_ERR_TYPE            // Тип ошибки при расчётах индикаторов
  {
   ERR_TYPE_NO_ERROR,         // Нет ошибки
   ERR_TYPE_NO_CYNC,          // Данные не синхронизированы
   ERR_TYPE_NO_DATA,          // Данные не загружены
   ERR_TYPE_NO_CALC,          // Расчёт не завершён
  };

Por esse código, será possível determinar externamente a ação necessária para tratar o erro.

Buffers do indicador

Aqui precisamos definir a quais buffers cada um pertence.

  1. Buffer da parte de cálculo. Ao criar um indicador, ele é criado na memória. Essa é a parte de cálculo do indicador. Ela possui seus próprios buffers, que são gerenciados pela sub-sistema do terminal. Pode-se acessar a parte de cálculo por meio de um handle, que é retornado após a criação bem-sucedida do indicador. No buffer de um indicador criado e calculado com sucesso, sempre há dados correspondentes à série temporal na qual o indicador foi calculado. Os dados neste buffer são organizados de forma que o índice zero corresponda à barra atual da série temporal na qual o indicador foi calculado.
    A função CopyBuffer() é usada para copiar dados a partir do buffer da parte de cálculo do indicador.

  2. Buffer do objeto indicador. Cada um dos objetos das classes de multi-indicadores criados terá seus próprios arrays-buffers de acordo com o número de buffers do indicador em questão. Esses arrays receberão dados a partir do buffer da parte de cálculo. Os buffers do objeto indicador serão gerenciados dentro do objeto da classe - inicializados, aumentados de acordo com o tamanho da série temporal na qual o indicador foi criado, e atualizados a cada novo tick. Ao copiar dados para o array do objeto indicador a partir do buffer da parte de cálculo usando CopyBuffer(), os dados serão organizados de forma que a barra atual esteja localizada no final do array (ArraySize()-1).

  3. Buffer da parte de desenho do indicador. Cada objeto indicador pode ser criado tanto em um EA quanto em um indicador personalizado. Ao criar multi-indicadores em EAs, para calcular os indicadores é necessário chamar o método que calcula o indicador, e para obter os dados calculados, acessar o índice de buffer necessário do objeto indicador. No entanto, em um indicador personalizado, também é necessário desenhar no gráfico os dados dos multi-indicadores criados dentro dele. É aqui que surge outro buffer: o de desenho. Este é o buffer designado para os dados a serem plotados em um indicador personalizado. Os dados armazenados nos buffers dos objetos indicadores serão exibidos nele. Para desenhar linhas no gráfico, será suficiente, a partir do indicador personalizado, chamar o método da classe coleção de indicadores que calcula os indicadores e, então, após um cálculo bem-sucedido, o método que colocará os dados do buffer do objeto indicador no buffer de desenho do indicador personalizado.

No objeto indicador, um buffer será representado por uma estrutura que contém tanto o array dinâmico quanto elementos para gerenciar esse array:

//--- struct
struct SBuffer                // Структура индикаторного буфера
  {
   double            array[];    // Массив-буфер индикатора
   double            init_value; // Инициализирующее значение
   int               shift;      // Сдвиг буфера по горизонтали
   string            descript;   // Описание буфера
   //--- (1) Устанавливает, (2) возвращает инициализирующее значение,
   void              SetInitValue(const double value) { init_value=value;                             }
   double            InitValue(void)                  { return init_value;                            }
   //--- (1) Устанавливает, (2) возвращает сдвиг буфера
   void              SetShift(const int value)        { shift=value;                                  }
   int               Shift(void)                      { return shift;                                 }
//--- (1) Изменяет размер массива буфера, (2) возвращает размер массива буфера,
//--- (3) инициализирует массив установленным "пустым" значением
   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);     }
  };

Alguns valores configurados de fora, por exemplo, durante a criação do indicador, precisam ser salvos para saber quais eram esses valores posteriormente. A maneira mais simples é gravá-los diretamente na estrutura. É exatamente isso que fazemos aqui — o "valor vazio" do buffer, estabelecido a partir do programa chamador, é salvo na estrutura do buffer na variável init_value. O deslocamento da linha do indicador, que é definido durante a criação da parte de cálculo do indicador, também é facilmente salvo aqui, para que possa ser conhecido dentro do objeto da classe; ele é salvo na variável shift. Aqui também é salvo a descrição do buffer, que é definida automaticamente durante a criação da parte de cálculo do indicador, de modo a corresponder ao nome do buffer de um indicador padrão semelhante. Posteriormente, esta descrição pode ser alterada.

As funções que retornam as descrições do estado da linha do indicador e erros no cálculo do indicador são necessárias apenas para exibir no log ou no painel informativo os estados das linhas dos indicadores e os erros em sua inicialização e cálculo:

//+------------------------------------------------------------------+
//| Возвращает описание состояния линии индикатора                   |
//+------------------------------------------------------------------+
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";
     }
  }
//+------------------------------------------------------------------+
//| Возвращает описание ошибки при расчёте индикатора                |
//+------------------------------------------------------------------+
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";
     }
  }

Todos os preparativos estão concluídos. Vamos nos concentrar diretamente na classe do objeto indicador multissímbolo e multiperíodo.

Vamos especificar no corpo da classe todas as variáveis e métodos privados, protegidos e públicos necessários para o funcionamento da classe, e então examinaremos seu propósito e implementação:

//+------------------------------------------------------------------+
//| Базовый класс мультисимвольного мультипериодного индикатора      |
//+------------------------------------------------------------------+
class CIndMSTF : public CObject
  {
private:
   ENUM_PROGRAM_TYPE m_program;           // Тип программы
   ENUM_INDICATOR    m_type;              // Тип индикатора
   ENUM_TIMEFRAMES   m_timeframe;         // Период графика
   string            m_symbol;            // Символ графика
   int               m_handle;            // Хэндл индикатора
   int               m_id;                // Идентификатор
   bool              m_success;           // Флаг успешного расчёта
   ENUM_ERR_TYPE     m_type_err;          // Тип ошибки при расчёте
   string            m_description;       // Пользовательское описание индикатора
   string            m_name;              // Наименование индикатора
   string            m_parameters;        // Описание параметров индикатора

protected:
   ENUM_IND_CATEGORY m_category;          // Категория индикатора
   MqlParam          m_param[];           // Массив параметров индикатора
   string            m_title;             // Заголовок (наименование индикатора + описание параметров)
   SBuffer           m_buffers[];         // Буферы индикатора
   int               m_digits;            // Digits значений индикатора
   int               m_limit;             // Количество баров, необходимое для просчёта индикатора на текущем тике
   int               m_rates_total;       // Количество доступных баров для просчёта индикатора
   int               m_prev_calculated;   // Количество просчитанных баров на прошлом вызове индикатора
   
//--- (1) Устанавливает наименование индикатора, (2) описание параметров
   void              SetName(const string name)                      { this.m_name=name;           }
   void              SetParameters(const string str)                 { this.m_parameters=str;      }
   
//--- Изменяет размер (1) указанного, (2) всех буферов индикатора
   bool              BufferResize(const uint buffer_num,const int new_buff_size);
   bool              BuffersResize(const int new_buff_size);
//--- Инициализирует (1) указанный, (2) все буферы индикатора
   bool              BufferInitialize(const uint buffer_num,const int new_buff_size);
   bool              BuffersInitialize(const int new_buff_size);
   
//--- Возвращает флаг равенства структуры одного параметра двух объектов
   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;
                       }
//--- Возвращает результат сравнения одного параметра двух объектов
   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:
//--- Создаёт расчётную часть индикатора, возвращает хэндл
   int               CreateIndicator(void);
//--- (1) Рассчитывает индикатор, (2) заполняет переданный массив-буфер (с учётом символа-периода графика) данными из буфера расчётной части индикатора данного класса
   bool              Calculate(void);
   bool              DataToBuffer(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const int limit,double &buffer[]);

//--- (1) Устанавливает (2) возвращает инициализирующее значение для указанного буфера
   void              SetBufferInitValue(const uint buffer_num,const double value);
   double            BufferInitValue(const uint buffer_num) const;

//--- (1) Устанавливает (2) возвращает значение сдвига для указанного буфера
   void              SetBufferShift(const uint buffer_num,const int value);
   double            BufferShift(const uint buffer_num) const;

//--- Возвращает данные указанного буфера (1) как есть, (2) относительно указанного символа/таймфрейма,
//--- (3) количество данных в указанном буфере, (4) состояние линии индикатора как есть в буфере расчётной части,
//--- (5) состояние линии индикатора с учётом символа/периода графика, описание состояния линии (6) как есть в буфере (7) с учётом символа/периода графика
   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);

//--- Возвращает (1) флаг успешности, (2) тип ошибки расчёта
   bool              IsSuccess(void)                           const { return this.m_success;               }
   ENUM_ERR_TYPE     TypeError(void)                           const { return this.m_type_err;              }
   
//--- Устанавливает (1) идентификатор, (2) Digits, (3) пользовательское описание, (4) описание указанного буфера
   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);

//--- Устанавливает индексацию массивов буферов расчётной части не как в таймсерии
   void              SetAsSeriesOff(void);
//--- Возвращает флаг серийности указанного буфера, (2) синхронизированности исторических данных по символу/периоду
   bool              IsSeries(const uint buffer_num) const;
   bool              IsSynchronized(void) const
                       {
                        return (bool)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_SYNCHRONIZED);
                       }
   
//--- Возвращает (1) таймфрейм, (2) символ, (3) наименование, (4) список параметров, (5) хэндл, (6) Digits
//--- количество (7) буферов, (8) баров, (9) идентификатор, (10) описание, (11) заголовок, (12) категорию,
//--- (13) количество параметрпов, (14) тип программы, описание (15) категории, (16) буфера индикатора
   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);

//--- Возвращает (1) структуру параметров по индексу из массива, (2) флаг программы-индикатора, (3) описание таймфрейма
   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);
                       }
//--- Возвращает количество рассчитанных данных
   int               Calculated(void)  const { return ::BarsCalculated(this.m_handle);                      }
   
//--- Виртуальный метод, возвращающий тип объекта (индикатора)
   virtual int       Type(void)                                const { return this.m_type;                  }
//--- Виртуальный метод сравнения двух объектов
   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);
                           //---Равенство всех параметров объектов
                           default                       : return(this.IsEqualIndicators(compared) ? 0 : -1);
                          }
                       }
//--- Возвращает флаг равенства параметров двух объектов-индикаторов
   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;
                       }
//--- Таймер
   void OnTimer(void);
   
//--- Конструктор/деструктор
                     CIndMSTF(){}
                     CIndMSTF(const ENUM_INDICATOR type,const uint buffers,const string symbol,const ENUM_TIMEFRAMES timeframe);
                    ~CIndMSTF();
  };

O propósito de cada variável e métodos é comentado no código da classe. Vamos estudar a implementação dos métodos.

Construtor da classe:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CIndMSTF::CIndMSTF(const ENUM_INDICATOR type,const uint buffers,const string symbol,const ENUM_TIMEFRAMES timeframe)
  {
//--- Запускаем таймер
   ::ResetLastError();
   if(!::EventSetTimer(1))
      ::PrintFormat("%s: EventSetTimer failed. Error %lu",__FUNCTION__,::GetLastError());
//--- Устанавливаем свойствам переданные в конструктор значения, или значения по умолчанию
   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;
//--- Устанавливаем массиву структуре буферов размер, равный количеству буферов индикатора
   ::ResetLastError();
   if(::ArrayResize(this.m_buffers,buffers)!=buffers)
      ::PrintFormat("%s: Buffers ArrayResize failed. Error  %lu"__FUNCTION__,::GetLastError());
//--- Устанавливаем "пустое" значение для каждого буфера по умолчанию (потом можно изменить)
   for(int i=0;i<(int)this.m_buffers.Size();i++)
      this.SetBufferInitValue(i,EMPTY_VALUE);
//--- Устанавливаем начальные значения переменным, участвующим в экономном расчёте индикатора
   this.m_prev_calculated=0;
   this.m_limit=0;
   this.m_rates_total=::Bars(this.m_symbol,this.m_timeframe);
//--- Если индикатор рассчитывается не на текущем графике - делаем запрос данных с нужного графика
//--- (первое обращение к данным запускает подкачку этих данных)
   datetime array[];
   if(symbol!=::Symbol() || timeframe!=::Period())
      ::CopyTime(this.m_symbol,this.m_timeframe,0,this.m_rates_total,array);
  }

No construtor da classe são passados o tipo do indicador, o número de buffers, o nome do símbolo e o período gráfico. Em seguida, para as variáveis são definidos valores padrão, o tamanho do array de buffers, e é definido um valor inicializador padrão - "valor vazio" como EMPTY_VALUE - para os arrays de buffers. Se o objeto indicador não for calculado no gráfico atual, então, no final do construtor, faremos uma chamada à função que começa a baixar do servidor os dados da série temporal sobre a qual o indicador é calculado.

Destruidor da classe:

//+------------------------------------------------------------------+
//| Деструктор                                                       |
//+------------------------------------------------------------------+
CIndMSTF::~CIndMSTF()
  {
//--- Удаляем таймер
   ::EventKillTimer();
//--- Освобождаем хэндл индикатора
   ::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());
//--- Освобождаем память массивов буферов
   for(int i=0;i<(int)this.BuffersTotal();i++)
      ::ArrayFree(this.m_buffers[i].array);
  }

No destruidor da classe, o temporizador é destruído, o handle do indicador é liberado, e a memória dos arrays de buffers é liberada.

Timer:

//+------------------------------------------------------------------+
//| Таймер                                                           |
//+------------------------------------------------------------------+
void CIndMSTF::OnTimer(void)
  {
//--- Если символ и таймфрейм индикатора такие же, как у текущего графика - уходим
   if(this.Symbol()==::Symbol() && this.Timeframe()==::Period())
      return;
//--- Объявляем переменные счётчиков таймера
   static int count1=0;
   static int count2=0;
//--- Если счётчик таймера 1 достиг заданного значения,
   if(count1>=TIMER_COUNT_1)
     {
      //--- вызываем функцию CopyTime (удержание таймсерии) и сбрасываем счётчик
      datetime array[1];
      ::CopyTime(this.m_symbol,this.m_timeframe,0,1,array);
      count1=0;
     }
//--- Если счётчик таймера 2 достиг заданного значения
   if(count2>=TIMER_COUNT_2)
     {
      static int count=0;
      //--- если прошлый расчёт индикатора был неуспешным - эмулируем тик с сообщением в журнал
      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;
              }
           }
        }
      //--- сбрасываем счётчик
      count2=0;
     }
//--- Увеличиваем счётчики таймеров
   count1++;
   count2++;
  }

No timer da classe, teremos dois contadores: um para manter a série temporal do indicador e o outro para a emulação de ticks durante os finais de semana. Se o indicador é calculado com base nos dados do símbolo/período atual do gráfico, o timer não é utilizado.

O próprio objeto indicador não é um indicador per se. É apenas uma wrapper sobre a parte de cálculo do indicador, permitindo gerenciá-la, recuperar dados dela e transferi-los ao programa. No objeto do multi-indicador, é necessário criar o próprio indicador. É esse o propósito do método para criar a parte de cálculo do indicador, retornando o handle obtido durante a criação:

//+------------------------------------------------------------------+
//| Создаёт индикатор, возвращает хэндл                              |
//+------------------------------------------------------------------+
int CIndMSTF::CreateIndicator(void)
  {
//--- Создаём наименование индикатора для вывода в журнал
   string name=::StringFormat("%s(%s,%s)",::StringSubstr(::EnumToString(this.m_type),4),this.m_symbol,this.TimeframeDescription());
//--- Создаём расчётную часть индикатора
   ::ResetLastError();
   this.m_handle=::IndicatorCreate(this.m_symbol,this.m_timeframe,this.m_type,this.m_param.Size(),this.m_param);
//--- Если расчётная часть не создана - выводим в журнал сообщение о неудачном создании индикатора
   if(this.m_handle==INVALID_HANDLE)
      ::PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,name,::GetLastError());
//--- Если индикатор создан - устанавливаем индексацию массивов индикатора не как у таймсерии
   else
      this.SetAsSeriesOff();
//--- Возвращаем хэндл созданного индикатора, либо -1 при неудаче
   return this.m_handle;
  }

Os buffers do objeto indicador são arrays dinâmicos dentro da estrutura do buffer, discutida anteriormente, que precisam ter seu tamanho alterado para acomodar constantemente o crescente volume de novos dados. Para isso, existem dois métodos: um método para alterar o tamanho do array de um buffer especificado e outro para alterar o tamanho de todos os buffers do objeto indicador simultaneamente.

Método que altera o tamanho do buffer indicador especificado:

//+------------------------------------------------------------------+
//| Изменяет размер указанного буфера индикатора                     |
//+------------------------------------------------------------------+
bool CIndMSTF::BufferResize(const uint buffer_num,const int new_buff_size)
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал и возвращаем 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;
     }
//--- Изменяем размер буфера
   ::ResetLastError();
   bool res=this.m_buffers[buffer_num].BuffResize(new_buff_size);
//--- При неудаче выводим сообщение в журнал
   if(!res)
      ::PrintFormat("%s::%s: Buffer(%lu) resize failed. Error %lu",__FUNCTION__,this.Title(),buffer_num,::GetLastError());
//--- Возвращаем результат изменения размера массива буфера
   return res;
  }

No método, é passado o número do buffer cujo array precisa ser modificado segundo um valor, que também é passado ao método. Se o número do buffer for especificado incorretamente, veremos um registro no log e o método retorna false.
Se o tamanho do array não puder ser alterado, uma mensagem aparece no log. Eventualmente, retorna-se o resultado da alteração do tamanho do array do buffer.

Método que altera o tamanho de todos os buffers do indicador:

//+------------------------------------------------------------------+
//| Изменяет размер всех буферов индикатора                          |
//+------------------------------------------------------------------+
bool CIndMSTF::BuffersResize(const int new_buff_size)
  {
//--- В цикле по всем буферам индикатора добавляем к переменной res результат изменения размера каждого очередного буфера
   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 res;
  }

Aqui, em um loop por todos os buffers do objeto indicador, o resultado da alteração do tamanho do array do próximo buffer é adicionado à variável res, cujo valor é eventualmente retornado pelo método.

De maneira similar, são feitos os métodos para a inicialização dos arrays de buffers do objeto indicador.

Método que inicializa o buffer especificado do indicador:

//+------------------------------------------------------------------+
//| Инициализирует указанный буфер индикатора                        |
//+------------------------------------------------------------------+
bool CIndMSTF::BufferInitialize(const uint buffer_num,const int new_buff_size)
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал и возвращаем 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;
     }
//--- Изменяем размер массива буфера
   bool res=this.BufferResize(buffer_num,new_buff_size);
//--- При удачном изменении размера инициализируем буфер установленным инициализирующим значением
   if(res)
      this.m_buffers[buffer_num].InitBuffer();
//--- Возвращаем результат
   return res;
  }

Aqui também verificamos se o número do buffer a ser inicializado foi especificado corretamente. Então, um novo tamanho é definido para o buffer e, se o tamanho for alterado com sucesso, o array é inicializado com um valor estabelecido para esse buffer.

Método que inicializatodos os buffers do indicador:

//+------------------------------------------------------------------+
//| Инициализирует все буферы индикатора                             |
//+------------------------------------------------------------------+
bool CIndMSTF::BuffersInitialize(const int new_buff_size)
  {
//--- В цикле по всем буферам индикатора добавляем к переменной res результат изменения размера каждого очередного буфера
//--- При удачном изменении размера инициализируем буфер установленным инициализирующим значением
   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 res;
  }

Em um loop por todos os buffers do objeto indicador, o resultado da alteração do tamanho do array do próximo buffer é adicionado à variável res. Se o tamanho for alterado com sucesso, o buffer é inicializado com um valor de inicialização estabelecido para ele. O método retorna o estado final da variável res, que terá o valor false se pelo menos um dos buffers não foi inicializado com sucesso.

Os demais métodos de configuração e retorno de valores dos buffers são idênticos aos examinados anteriormente:

//+------------------------------------------------------------------+
//| Устанавливает инициализирующее значение для указанного буфера    |
//+------------------------------------------------------------------+
void CIndMSTF::SetBufferInitValue(const uint buffer_num,const double value)
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал и уходим
   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;
     }
//--- Устанавливаем новое инициализирующее значение для указанного буфера
   this.m_buffers[buffer_num].SetInitValue(value);
  }
//+------------------------------------------------------------------+
//| Возвращает инициализирующее значение указанного буфера           |
//+------------------------------------------------------------------+
double CIndMSTF::BufferInitValue(const uint buffer_num) const
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал
   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);
      //--- Если у индикатора есть буферы, то возвращаем инициализирующее значение самого первого, иначе - EMPTY_VALUE
      return(this.BuffersTotal()>0 ? this.BufferInitValue(0) : EMPTY_VALUE);
     }
//--- Возвращаем инициализирующее значение запрошенного буфера
   return this.m_buffers[buffer_num].InitValue();
  }
//+------------------------------------------------------------------+
//| Устанавливает значение сдвига для указанного буфера              |
//+------------------------------------------------------------------+
void CIndMSTF::SetBufferShift(const uint buffer_num,const int value)
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал и уходим
   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;
     }
//--- Устанавливаем значение сдвига для буфера
   this.m_buffers[buffer_num].SetShift(value);
  }
//+------------------------------------------------------------------+
//| Возвращает значение сдвига указанного буфера                     |
//+------------------------------------------------------------------+
double CIndMSTF::BufferShift(const uint buffer_num) const
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал
   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);
      //--- Если у индикатора есть буферы, то возвращаем значение сдвига самого первого, иначе - 0
      return(this.BuffersTotal()>0 ? this.m_buffers[0].Shift() : 0);
     }
//--- Возвращаем значение сдвига запрошенного буфера
   return this.m_buffers[buffer_num].Shift();
  }

A lógica dos métodos é detalhadamente descrita nos comentários do código.

Método para o cálculo dos dados do indicador: O indicador calculado (sua parte calculada) possui um buffer que contém todos os dados calculados do indicador. É necessário copiar os dados do buffer da parte de cálculo para os arrays-buffers do objeto indicador. Além disso, é necessário realizar um cálculo eficiente, o que envolve copiar o buffer inteiro apenas no primeiro lançamento ou quando houver mudanças nos dados históricos.

Esse cálculo, que foi examinado anteriormente, também deve ser realizado no método de cálculo do objeto indicador. Então, em caso de erros, o método retornará false, e o programa que o chama deverá reagir a isso — saindo do manipulador (OnTick no EA e OnCalculate no indicador) até a chegada do próximo tick. Consideraremos como erros que exigem o retorno do método o início do carregamento de dados históricos, o carregamento incompleto do histórico, o cálculo incompleto do indicador e erros na cópia de dados a partir do buffer da parte de cálculo para o buffer do objeto indicador. O método armazenará o código do erro em uma variável, para que o programa que o chama possa lê-lo e processá-lo corretamente.

Método que preenche os buffers do objeto indicador com dados a partir do buffer da parte de cálculo:

//+------------------------------------------------------------------+
//| Заполняет буферы объекта данными из буфера расчётной части       |
//+------------------------------------------------------------------+
bool CIndMSTF::Calculate(void)
  {
//--- Устанавливаем флагу успешности значение true, а типу ошибки - её отсутствие
   this.m_success=true;
   this.m_type_err=ERR_TYPE_NO_ERROR;
//--- Если данные ещё не синхронизированы с торговым сервером,
   if(!this.IsSynchronized())
     {
      //--- Выводим в журнал сообщение о несинхронизированности,
      ::PrintFormat("%s::%s: Waiting for data to sync...",__FUNCTION__,this.Title());
      //--- устанавливаем тип ошибки, к флагу ошибки добавляем false и возвращаем false
      this.m_type_err=ERR_TYPE_NO_CYNC;
      this.m_success &=false;
      return false;
     }
//--- Если метод Calculated вернул -1, это означает начало закачки данных
   if(this.Calculated()==WRONG_VALUE)
     {
      //--- Выводим в журнал сообщение о начале закачки данных,
      ::PrintFormat("%s::%s: Start downloading data by %s/%s. Waiting for the next tick...",__FUNCTION__,this.Title(),this.m_symbol,this.TimeframeDescription());
      //--- устанавливаем тип ошибки, к флагу ошибки добавляем false и возвращаем false
      this.m_type_err=ERR_TYPE_NO_DATA;
      this.m_success &=false;
      return false;
     }
//--- Если метод Calculated вернул 0, это означает, что индикатор ещё не рассчитан
   if(this.Calculated()==0)
     {
      //--- Выводим в журнал сообщение об ожидании расчёта индикатора,
      ::PrintFormat("%s::%s: Waiting for a new tick and when the indicator will be calculated...",__FUNCTION__,this.Title());
      //--- устанавливаем тип ошибки, к флагу ошибки добавляем false и возвращаем false
      this.m_type_err=ERR_TYPE_NO_CALC;
      this.m_success &=false;
      return false;
     }
//--- Получаем количество баров данных по символу/периоду индикатора
   int bars=::Bars(this.m_symbol,this.m_timeframe);
//--- Если функция Bars вернула нулевое значение, что часто бывает на выходных, рассчитаем доступное количество баров
   if(bars==0)
     {
      //--- Получим дату самого первого доступного бара в истории по символу/периоду
      datetime firstdate=(datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_FIRSTDATE);
      //--- Получим дату последнего (текущего) бара в истории по символу/периоду
      datetime lastdate=(datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_LASTBAR_DATE);
      //--- Рассчитаем количество баров между первой и последней датами истории
      int sec=::PeriodSeconds(this.m_timeframe);
      ulong date_bars=(((ulong)lastdate-(ulong)firstdate)/(sec>0 ? sec : 1))+1;
      //--- В переменную bars запишем меньшее значение из рассчитанного количества баров и максимального количества баров, доступных в терминале
      bars=(int)fmin(date_bars,::TerminalInfoInteger(TERMINAL_MAXBARS));
     }
//--- Запишем в m_rates_total полученное количество доступных баров
   if(this.m_rates_total!=bars)
      this.m_rates_total=bars;
//--- Если количество доступных баров получено, и их два и меньше,
   if(this.m_rates_total>=0 && this.m_rates_total<3)
     {
      //--- Выведем в журнал сообщение о слишком маленьком количестве доступных баров
      ::PrintFormat("%s::%s: Not enough data for calculation: %ld bars. Waiting for the next tick...",__FUNCTION__,this.Title(),this.m_rates_total);
      //--- установим тип ошибки, к флагу ошибки добавим false и вернём false
      this.m_type_err=ERR_TYPE_NO_DATA;
      this.m_success &=false;
      return false;
     }

//--- Рассчитаем количество необходимых баров для расчёта индикатора
//--- Либо вся доступная история, либо 1 при открытии нового бара, либо 0 на текущем тике
   this.m_limit=this.m_rates_total-this.m_prev_calculated;
   this.m_prev_calculated=this.Calculated();

//--- Объявляем массив размером 2 для получения в него данных из буфера расчётной части индикатора
//--- Получать будем всегда по два бара - прошлый и текущий
   double array[2];
//--- Получаем количество буферов индикатора
   int total=(int)this.BuffersTotal();
//--- Если рассчитанное значение m_limit больше 1 - значит это либо первый запуск, либо изменения в исторических данных
//--- В этом случае необходим полный перерасчёт индикатора
   if(this.m_limit>1)
     {
      //--- В цикле по количеству буферов индикатора
      for(int i=0;i<total;i++)
        {
         //--- изменяем размер массива буфера индикатора и инициализируем его установленным для этого буфера "пустым" значением
         this.BufferInitialize(i,this.m_rates_total);
         ::ResetLastError();
         //--- Копируем все имеющиеся исторические данные из массива расчётной части индикатора в массив-буфер объекта индикатора
         int copied=::CopyBuffer(this.m_handle,i,-this.m_buffers[i].Shift(),this.m_rates_total,this.m_buffers[i].array);
         //--- Если скопированы не все данные
         if(copied!=this.m_rates_total)
           {
            //--- Если CopyBuffer вернула -1, то это означает начало загрузки исторических данных
            //--- выведем сообщение об этом в журнал
            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());
            //--- В любом ином случае - ещё не все данные удалось скопировать
            //--- выведем сообщение об этом в журнал
            else
               ::PrintFormat("%s::%s: Not all data was copied. Data available: %lu, total copied: %ld",__FUNCTION__,this.Title(),this.m_rates_total,copied);
            //--- Запишем в тип ошибки отсутствие данных
            this.m_type_err=ERR_TYPE_NO_DATA;
            //--- Добавим к результату значение false и вернём false для выхода из метода и ожидания следующего тика
            this.m_success &=false;
            return false;
           }
        }
      //--- Если вышли из цикла копирования всех буферов индикатора, значит всё успешно - вернём true
      return true;
     }
//--- Если рассчитанное значение m_limit меньше, либо равно 1 - значит это либо открытие нового бара (m_limit==1), либо текущий тик (m_limit==0)
//--- В этом случае необходимо рассчитать два бара - первый и текущий
   if(this.m_limit<=1)
     {
      //--- В цикле по количеству буферов индикатора
      for(int i=0;i<total;i++)
        {
         //--- Если это открытие нового бара и не удалось изменить размер буфера индикатора,
         if(this.m_limit==1 && !this.BufferResize(i,this.m_rates_total))
           {
            //--- добавим к переменной m_success значение false и вернём false
            //--- При этом сообщение об ошибке будет выведено в журнал из метода BufferResize
            this.m_success &=false;
            return false;
           }
         //--- Если не удалось скопировать два бара из буфера расчётной части индикатора,
         ::ResetLastError();
         if(::CopyBuffer(this.m_handle,i,-this.m_buffers[i].Shift(),2,array)!=2)
           {
            //--- сообщим об этом в журнал, добавим к переменной m_success значение false и вернём false
            ::PrintFormat("%s::%s: CopyBuffer(%lu) failed. Error %lu",__FUNCTION__,this.Title(),i,::GetLastError());
            this.m_success &=false;
            return false;
           }
         //--- Если дошли сюда, значит копирование успешно -
         //--- скопируем в буфер объекта-индикатора данные из массива array[], в который были скопированы последние два бара
         this.m_buffers[i].array[this.DataTotal(i)-1]=array[1];
         this.m_buffers[i].array[this.DataTotal(i)-2]=array[0];
        }
      //--- Успешно
      return true;
     }
//--- Неопределённый вариант limit - возвращаем false
   return false;
  }

Toda a lógica do método é detalhadamente descrita nos comentários de cada bloco de código. O método deve ser chamado a partir do programa e, se retornar false, é necessário sair de OnTick ou OnCalculate até o próximo tick.

Se o método for executado sem erros, o buffer do objeto indicador conterá dados prontos para uso. Eles poderão ser acessados por meio de métodos que serão examinados posteriormente.

Nos indicadores, existem métodos especiais para transferir dados a partir do buffer de um objeto indicador, preenchido por este método, para os buffers de desenho do indicador. Estes métodos exibem os dados no buffer de desenho da mesma forma que estão armazenados no buffer do objeto indicador, ou considerando o símbolo/período gráfico. Esses métodos podem ser usados para mostrar os dados calculados no objeto indicador no gráfico atual.

Método que preenche o array passado com dados a partir do buffer da classe:

//+------------------------------------------------------------------+
//| Заполняет переданный массив данными из буфера класса             |
//+------------------------------------------------------------------+
bool CIndMSTF::DataToBuffer(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const int limit,double &buffer[])
  {
//--- Устанавливаем флаг успешности
   this.m_success=true;
//--- Получаем направление индексации переданного в метод массива буфера и,
//--- если индексация не как у таймсерии, - устанавливаем индексацию как у таймсерии
   bool as_series=::ArrayGetAsSeries(buffer);
   if(!as_series)
      ::ArraySetAsSeries(buffer,true);
//--- Устанавливаем наименование символа и значение таймфрейма, переданные в метод
   string symbol=(symbol_to=="" || symbol_to==NULL ? ::Symbol() : symbol_to);
   ENUM_TIMEFRAMES timeframe=(timeframe_to==PERIOD_CURRENT ? ::Period() : timeframe_to);
   datetime array[2];
//--- Если это первый запуск, или изменения в истории - инициальзируем массив буфера, переданный в метод
   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));
     }
//--- Устанавливаем значение счётчика цикла (не более максимального количества баров в терминале на графике)
   int count=(limit<=1 ? 2 : ::fmin(::TerminalInfoInteger(TERMINAL_MAXBARS),limit));
//--- В цикле от нулевого бара до значения счётчика цикла
   for(int i=0;i<count;i++)
     {
      //--- Если таймфрейм графика совпадает с таймфреймом объекта класса - заполняем буфер напрямую из массива объекта класса
      if(timeframe==::Period() && this.m_timeframe==::Period())
         buffer[i]=this.GetData(buffer_num,i);
      //--- Иначе, если таймфрейм графика не равен таймфрейму объекта класса
      else
        {
         //--- Узнаём какому времени данного класса принадлежит бар текущего таймфрейма графика, соответствующий индексу цикла
         ::ResetLastError();
         if(::CopyTime(symbol,timeframe,i,2,array)!=2)
           {
            //--- Если данных нет в терминале - идём дальше
            if(::GetLastError()==4401)
               continue;
            //--- Ошибка получения существующих данных - возвращаем false
            this.m_success &=false;
            return false;
           }
         //--- По времени бара текущего таймфрейма графика находим соответствующий индекс бара периода графика объекта класса
         ::ResetLastError();
         int bar=::iBarShift(this.m_symbol,this.m_timeframe,array[0]);
         if(bar==WRONG_VALUE)
           {
            this.m_success &=false;
            continue;
           }
         //--- Если это исторические данные (не первый и не нулевой бар) -
         //--- записываем в буфер индикатора по индексу цикла значение, полученное из буфера расчётной части
         if(i>1)
            buffer[i]=this.GetData(buffer_num,bar);
         //--- Если это текущий (нулевой) или предыдущий (первый) бар
         else
           {
            //--- Получаем время баров 0 и 1 по символу/таймфрейму объекта класса
            if(::CopyTime(this.m_symbol,this.m_timeframe,0,2,array)!=2)
              {
               this.m_success &=false;
               return false;
              }
            //--- Получаем по времени индексы текущего и предыдущего баров на графике, символ/период которого передан в метод
            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(timeframe<this.m_timeframe)
              {
               //--- в цикле от бара с меньшим временем до текущего бара графика заполняем буфер данными из двух последних ячеек массива буфера индикатора
               for(int j=bar1;j>=0;j--)
                  buffer[j]=this.GetData(buffer_num,(j>bar0 ? 1 : 0));
              }
            //--- Если таймфрейм графика старше таймфрейма объекта класса,
            else
              {
               //--- Получаем время текущего и предыдущего баров по символу/таймфрейму текущего графика
               if(::CopyTime(symbol,timeframe,0,2,array)!=2)
                 {
                  this.m_success &=false;
                  return false;
                 }
               //--- Получаем по времени индексы баров в буфере расчётной части индикатора, соответствующие времени текущего и предыдущего баров на графике
               int bar0=::iBarShift(this.m_symbol,this.m_timeframe,array[1]);
               int bar1=::iBarShift(this.m_symbol,this.m_timeframe,array[0]);
               //--- Записываем в буфер индикатора по индексам 1 и 0 значения из соответствующих индексов буфера расчётной части
               buffer[1]=this.GetData(buffer_num,bar1);
               buffer[0]=this.GetData(buffer_num,bar0);
              } 
           }
        }
     }
//--- Устанавливаем изначальную индексацию переданного в метод массива буфера
   ::ArraySetAsSeries(buffer,as_series);
//--- Успешно
   return true;
  }

A lógica do método é detalhadamente explicada na listagem. A essência do método é calcular corretamente quais barras do gráfico atual precisam ser preenchidas com dados do array do buffer do indicador calculado em outro timeframe. No método, o último parâmetro passado é o array do buffer de desenho do indicador personalizado, que deve mostrar o indicador calculado em outro símbolo/período.

Método que retorna os dados do buffer especificado como estão:

//+------------------------------------------------------------------+
//| Возвращает данные указанного буфера как есть                     |
//+------------------------------------------------------------------+
double CIndMSTF::GetData(const uint buffer_num,const int index) const
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал
   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);
      //--- Если у индикатора есть буферы, то возвращаем "пустое" значение самого первого, иначе - EMPTY_VALUE
      return(this.BuffersTotal()>0 ? this.BufferInitValue(0) : EMPTY_VALUE);
     }
//--- Если указан не правильный индекс - возвращаем "пустое" значение указанного буфера
   if(index<0 || index>(int)this.DataTotal(buffer_num)-1)
      return this.BufferInitValue(buffer_num);
//--- Рассчитываем реальный индекс в массиве-буфере и возвращаем значение по этому индексу
   int n=int(this.DataTotal(buffer_num)-1-index);
   return this.m_buffers[buffer_num].array[n];
  }

O método simplesmente retorna os dados a partir do buffer do objeto indicador pelo índice especificado.

Método que retornaos dados do buffer especificado para o símbolo/timeframe indicado:

//+-------------------------------------------------------------------+
//| Возвращает данные указанного буфера на указанный символ/таймфрейм |
//+-------------------------------------------------------------------+
double CIndMSTF::GetDataTo(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const int index) const
  {
//--- Если указан текущий символ/период графика
   if(timeframe_to==::Period() && this.m_timeframe==::Period() && symbol_to==::Symbol() && this.m_symbol==::Symbol())
      return this.GetData(buffer_num,index);
//--- Узнаём какому времени данного класса принадлежит бар текущего таймфрейма графика, соответствующий индексу цикла
   datetime array[];
   if(::CopyTime(symbol_to,timeframe_to,index,1,array)!=1)
      return this.BufferInitValue(buffer_num);
//--- По времени бара текущего таймфрейма графика находим соответствующий индекс бара периода графика данного класса
   int bar=iBarShift(this.m_symbol,this.m_timeframe,array[0]);
//--- Если бар не найден - возвращаем "пустое" значение, установленное для буфера
   if(bar==WRONG_VALUE)
      return this.BufferInitValue(buffer_num);
//--- Возвращаем значение из буфера объекта-индикатора по найденному индексу
   return this.GetData(buffer_num,bar);
  }

O método encontra o índice da barra da série temporal na qual o indicador foi calculado, índice esse que corresponde ao símbolo/período passado para o método, e retorna os dados a partir do buffer do objeto indicador.

Método que retorna o estado da linha do indicador como está:

//+------------------------------------------------------------------+
//| Возвращает состояние линии индикатора как есть                   |
//+------------------------------------------------------------------+
ENUM_LINE_STATE CIndMSTF::BufferLineState(const uint buffer_num,const int index) const
  {
//--- Получаем значения линии индикатора со смещением (0,1,2) относительно переданного индекса
   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(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Разворот линии вверх (value2>value1 && value0>value1)
   if(::NormalizeDouble(value2-value1,this.m_digits)>0 && ::NormalizeDouble(value0-value1,this.m_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Направление линии вверх (value2<=value1 && value0>value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)<=0 && ::NormalizeDouble(value0-value1,this.m_digits)>0)
      return LINE_STATE_UP;
//--- Остановка направления линии вверх (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;
//--- Разворот линии вниз (value2<value1 && value0<value1)
   if(::NormalizeDouble(value2-value1,this.m_digits)<0 && ::NormalizeDouble(value0-value1,this.m_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Направление линии вниз (value2>=value1 && value0<value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)>=0 && ::NormalizeDouble(value0-value1,this.m_digits)<0)
      return LINE_STATE_DOWN;
//--- Остановка направления линии вниз (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;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }

O método determina o estado da linha do objeto indicador pelos dados em seu buffer e retorna o valor encontrado.

Método que retorna o estado da linha do indicador considerando o símbolo/período:

//+------------------------------------------------------------------+
//| Возвращает состояние линии индикатора с учётом символа/периода   |
//+------------------------------------------------------------------+
ENUM_LINE_STATE CIndMSTF::BufferLineState(const string symbol_from,const ENUM_TIMEFRAMES timeframes_from,const uint buffer_num,const int index) const
  {
//--- Определяем переданные в метод символ/период графика
   string symbol=(symbol_from=="" || symbol_from==NULL ? ::Symbol() : symbol_from);
   ENUM_TIMEFRAMES timeframe=(timeframes_from==PERIOD_CURRENT ? ::Period() : timeframes_from);
//--- Если получаем данные от символа/периода, равные текущему графику - возвращаем состояние из буфера "как есть"
   if(symbol==::Symbol() && symbol==this.m_symbol && timeframe==::Period() && timeframe==this.m_timeframe)
      return this.BufferLineState(buffer_num,index);
//--- Объявляем переменные для поиска нужных баров на текущем графике
   datetime array[1];
   int      bar0=WRONG_VALUE;
   int      bar1=WRONG_VALUE;
   int      bar2=WRONG_VALUE;

//--- Получаем время первого бара на графике
   ::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;
     }
//--- Получаем индекс первого бара в буфере объекта-индикатора по времени открытия бара на графике
   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;
     }
//--- Получаем время второго бара на графике
   ::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;
     }
//--- Получаем индекс второго бара в буфере объекта-индикатора по времени открытия бара на графике
   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;
     }
//--- Получаем время третьего бара на графике
   ::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;
     }
//--- Получаем индекс третьего бара в буфере объекта-индикатора по времени открытия бара на графике
   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;
     }
     
//--- Получаем значения линии индикатора со смещением (0,1,2) относительно переданного индекса
   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(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Разворот линии вверх (value2>value1 && value0>value1)
   if(::NormalizeDouble(value2-value1,this.m_digits)>0 && ::NormalizeDouble(value0-value1,this.m_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Направление линии вверх (value2<=value1 && value0>value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)<=0 && ::NormalizeDouble(value0-value1,this.m_digits)>0)
      return LINE_STATE_UP;
//--- Остановка направления линии вверх (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;
//--- Разворот линии вниз (value2<value1 && value0<value1)
   if(::NormalizeDouble(value2-value1,this.m_digits)<0 && ::NormalizeDouble(value0-value1,this.m_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Направление линии вниз (value2>=value1 && value0<value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)>=0 && ::NormalizeDouble(value0-value1,this.m_digits)<0)
      return LINE_STATE_DOWN;
//--- Остановка направления линии вниз (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;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }

O método permite obter o estado da linha do objeto indicador em relação ao gráfico atual. Se o objeto indicador for calculado com base em dados de um timeframe superior ao do gráfico atual, então sua linha será "esticada" ao longo das barras do gráfico atual. O método permite obter corretamente o estado da linha do indicador na barra especificada do gráfico atual.

Método que retornao estado da linha em relação a um nível especificado:

//+------------------------------------------------------------------+
//| Возвращает состояние линии относительно указанного уровня        |
//+------------------------------------------------------------------+
ENUM_LINE_STATE CIndMSTF::BufferLineStateRelative(const int buffer_num,const int index,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Получаем значения линии индикатора со смещением (0,1) относительно переданного индекса
   const double value0=this.GetData(buffer_num,index);
   const double value1=this.GetData(buffer_num,index+1);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Определяем второй сравниваемый уровень
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- Линия находится под уровнем (value1<level && value0<level0)
   if(::NormalizeDouble(value1-level,this.m_digits)<0 && ::NormalizeDouble(value0-level0,this.m_digits)<0)
      return LINE_STATE_BELOW;
//--- Линия находится над уровнем (value1>level && value0>level0)
   if(::NormalizeDouble(value1-level,this.m_digits)>0 && ::NormalizeDouble(value0-level0,this.m_digits)>0)
      return LINE_STATE_ABOVE;
//--- Линия пересекла уровень снизу-вверх (value1<=level && value0>level0)
   if(::NormalizeDouble(value1-level,this.m_digits)<=0 && ::NormalizeDouble(value0-level0,this.m_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- Линия пересекла уровень сверху-вниз (value1>=level && value0<level0)
   if(::NormalizeDouble(value1-level,this.m_digits)>=0 && ::NormalizeDouble(value0-level0,this.m_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- Линия коснулась уровня снизу (value1<level0 && value0==level0)
   if(::NormalizeDouble(value1-level,this.m_digits)<0 && ::NormalizeDouble(value0-level0,this.m_digits)==0)
      return LINE_STATE_TOUCH_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;
//--- Линия равна значению уровня (value1==level0 && value0==level0)
   if(::NormalizeDouble(value1-level,this.m_digits)==0 && ::NormalizeDouble(value0-level0,this.m_digits)==0)
      return LINE_STATE_EQUALS;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }

Método que retornao estado da linha em relação a um nível especificado no símbolo/período indicado do gráfico:

//+------------------------------------------------------------------+
//| Возвращает состояние линии относительно указанного уровня        |
//| на указанном символе/периоде графика                             |
//+------------------------------------------------------------------+
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)
  {
//--- Определяем переданные в метод символ/период графика
   string symbol=(symbol_from=="" || symbol_from==NULL ? ::Symbol() : symbol_from);
   ENUM_TIMEFRAMES timeframe=(timeframes_from==PERIOD_CURRENT ? ::Period() : timeframes_from);
//--- Получаем значения линии индикатора со смещением (0,1) относительно переданного индекса
   const double value0=this.GetDataTo(symbol,timeframe,buffer_num,index);
   const double value1=this.GetDataTo(symbol,timeframe,buffer_num,index+1);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Определяем второй сравниваемый уровень
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- Линия находится под уровнем (value1<level && value0<level0)
   if(::NormalizeDouble(value1-level,this.m_digits)<0 && ::NormalizeDouble(value0-level0,this.m_digits)<0)
      return LINE_STATE_BELOW;
//--- Линия находится над уровнем (value1>level && value0>level0)
   if(::NormalizeDouble(value1-level,this.m_digits)>0 && ::NormalizeDouble(value0-level0,this.m_digits)>0)
      return LINE_STATE_ABOVE;
//--- Линия пересекла уровень снизу-вверх (value1<=level && value0>level0)
   if(::NormalizeDouble(value1-level,this.m_digits)<=0 && ::NormalizeDouble(value0-level0,this.m_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- Линия пересекла уровень сверху-вниз (value1>=level && value0<level0)
   if(::NormalizeDouble(value1-level,this.m_digits)>=0 && ::NormalizeDouble(value0-level0,this.m_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- Линия коснулась уровня снизу (value1<level0 && value0==level0)
   if(::NormalizeDouble(value1-level,this.m_digits)<0 && ::NormalizeDouble(value0-level0,this.m_digits)==0)
      return LINE_STATE_TOUCH_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;
//--- Линия равна значению уровня (value1==level0 && value0==level0)
   if(::NormalizeDouble(value1-level,this.m_digits)==0 && ::NormalizeDouble(value0-level0,this.m_digits)==0)
      return LINE_STATE_EQUALS;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }

Sobre os métodos de obtenção do estado das linhas do indicador, você pode ler mais detalhadamente no artigo sobre a integração de osciladores a Expert Advisors.

Outros métodos da classe:

//+------------------------------------------------------------------+
//| Возвращает описание категории                                    |
//+------------------------------------------------------------------+
string CIndMSTF::CategoryDescription(void)
  {
//--- Создаём из перечисления ENUM_IND_CATEGORY наименование категории и возвращаем полученный текст
   string category=::StringSubstr(::EnumToString(this.m_category),13);
   if(category.Lower())
      category.SetChar(0,ushort(category.GetChar(0)-0x20));
   return category;
  }
//+------------------------------------------------------------------+
//| Возвращает описание буфера индикатора                            |
//+------------------------------------------------------------------+
string CIndMSTF::BufferDescription(const uint buffer_num)
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал и возвращаем пустую строку
   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 "";
     }
//--- Если у индикатора есть буферы, возвращаем описание указанного буфера, иначе - описание индикатора
   return(this.BuffersTotal()>0 ? this.m_buffers[buffer_num].descript : this.m_title);
  }
//+------------------------------------------------------------------+
//| Устанавливает описание буфера индикатора                         |
//+------------------------------------------------------------------+
void CIndMSTF::SetBufferDescription(const uint buffer_num,const string descr)
  {
//--- Если индикатор не имеет буферов - уходим
   if(this.BuffersTotal()==0)
      return;
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал и уходим
   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;
     }
//--- Записываем в указанный буфер переданное в метод описание
   this.m_buffers[buffer_num].descript=descr;
  }
//+------------------------------------------------------------------+
//| Отключает индексацию массивов буферов как у таймсерии            |
//+------------------------------------------------------------------+
void CIndMSTF::SetAsSeriesOff(void)
  {
//--- В цикле по всем буферам индикатора отключаем флаг серийности массивов
   for(int i=0;i<(int)this.BuffersTotal();i++)
      ::ArraySetAsSeries(this.m_buffers[i].array,false);
  }
//+------------------------------------------------------------------+
//| Возвращает флаг серийности указанного буфера                     |
//+------------------------------------------------------------------+
bool CIndMSTF::IsSeries(const uint buffer_num) const
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал и возвращаем 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 (bool)::ArrayGetAsSeries(this.m_buffers[buffer_num].array);
  }
//+------------------------------------------------------------------+
//| Возвращает количество данных указанного буфера                   |
//+------------------------------------------------------------------+
uint CIndMSTF::DataTotal(const uint buffer_num) const
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал и возвращаем ноль
   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 this.m_buffers[buffer_num].array.Size();
  }

A classe base do objeto indicador com vários símbolos/períodos está pronta. A classe contém toda a funcionalidade necessária para trabalhar com indicadores construídos em dados de séries temporais que não pertencem ao gráfico atual.

Para criar diferentes tipos de indicadores técnicos, é necessário criar classes derivadas da classe base recém-criada. Nos construtores das classes derivadas, serão indicados os parâmetros e propriedades que são característicos do tipo de indicador sendo criado.


Classes de indicadores por tipo

As classes derivadas da base serão as mais simples e conterão apenas o construtor. No construtor da classe, serão passados o símbolo/período gráfico no qual o indicador é calculado, e os parâmetros de entrada, característicos desse tipo de indicadores. Na linha de inicialização do construtor, os parâmetros serão passados para o construtor da classe pai.

Exemplo de classe que cria um objeto indicador, que não contém parâmetros:

//+------------------------------------------------------------------+
//| Класс индикатора Accelerator Oscillator                          |
//+------------------------------------------------------------------+
class CIndAC : public CIndMSTF
  {
public:
//--- Конструктор
   CIndAC(const string symbol,const ENUM_TIMEFRAMES timeframe) : CIndMSTF(IND_AC,1,symbol,timeframe)
     {
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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));
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
      this.SetParameters(param);
      this.SetName("AC");
      this.SetDescription("Accelerator Oscillator");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_WILLIAMS;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };

Na linha de inicialização, o tipo de indicador, o número de buffers, o símbolo e o período do gráfico são passados para a classe pai, nos quais o indicador deve ser calculado. No corpo da classe, é criada uma linha com a descrição dos parâmetros do indicador. Neste caso, se o indicador é criado com dados do gráfico atual, então a linha de parâmetros estará vazia. Caso contrário, ela conterá o símbolo e o período do gráfico, por exemplo "(EURUSD,H1)". Em seguida, no corpo do construtor, todos os parâmetros característicos desse tipo de indicador são definidos (aqui é o indicador Accelerator Oscillator).

Cada indicador tem a possibilidade de definir o número de dígitos após a vírgula, exibidos na janela de dados e no gráfico do instrumento. No construtor desta classe, não há definição de Digits, pois este valor, igual a Digits do símbolo no qual o indicador é calculado, é definido no construtor da classe pai. Se for necessário definir um valor diferente de Digits para o indicador, isso ou é feito nos construtores das classes daqueles indicadores onde os valores de Digits diferem dos Digits do símbolo, ou pode ser alterado após a criação do objeto indicador por meio do método SetDigits().

Classe do indicador que possui parâmetros:

//+------------------------------------------------------------------+
//| Класс индикатора Accumulation/Distribution                       |
//+------------------------------------------------------------------+
class CIndAD : public CIndMSTF
  {
public:
//--- Конструктор
   CIndAD(const string symbol,const ENUM_TIMEFRAMES timeframe,
          const ENUM_APPLIED_VOLUME applied_volume // используемый объем
         ) : CIndMSTF(IND_AD,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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));
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };

Aqui existem parâmetros, e eles precisam ser incluídos na estrutura-array de parâmetros de entrada do indicador MqlParam. E aqui mesmo definimos o valor de Digits do indicador, que é estabelecido para o Accumulation/Distribution padrão.

Lista completa de todas as classes derivadas da classe base do indicador com vários símbolos/períodos:

//+------------------------------------------------------------------+
//| Класс индикатора Accelerator Oscillator                          |
//+------------------------------------------------------------------+
class CIndAC : public CIndMSTF
  {
public:
//--- Конструктор
   CIndAC(const string symbol,const ENUM_TIMEFRAMES timeframe) : CIndMSTF(IND_AC,1,symbol,timeframe)
     {
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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));
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
      this.SetParameters(param);
      this.SetName("AC");
      this.SetDescription("Accelerator Oscillator");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_WILLIAMS;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Accumulation/Distribution                       |
//+------------------------------------------------------------------+
class CIndAD : public CIndMSTF
  {
public:
//--- Конструктор
   CIndAD(const string symbol,const ENUM_TIMEFRAMES timeframe,
          const ENUM_APPLIED_VOLUME applied_volume // используемый объем
         ) : CIndMSTF(IND_AD,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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));
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Average Directional Movement Index              |
//+------------------------------------------------------------------+
class CIndADX : public CIndMSTF
  {
public:
//--- Конструктор
   CIndADX(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int adx_period                    // период усреднения
          ) : CIndMSTF(IND_ADX,3,symbol,timeframe)
     {
      // Номера буферов: 0 - MAIN_LINE, 1 - PLUSDI_LINE, 2 - MINUSDI_LINE
      //--- Устанавливаем размер массива параметров и заполняем его
      ::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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- записываем описания буферов линий MAIN_LINE, PLUSDI_LINE и MINUSDI_LINE
      this.SetBufferDescription(MAIN_LINE,this.m_title);
      this.SetBufferDescription(PLUSDI_LINE,"+DI");
      this.SetBufferDescription(MINUSDI_LINE,"-DI");
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Average Directional Movement Index Wilder       |
//+------------------------------------------------------------------+
class CIndADXW : public CIndMSTF
  {
public:
//--- Конструктор
   CIndADXW(const string symbol,const ENUM_TIMEFRAMES timeframe,
            const int adx_period                      // период усреднения
           ) : CIndMSTF(IND_ADXW,3,symbol,timeframe)
     {
      // Номера буферов: 0 - MAIN_LINE, 1 - PLUSDI_LINE, 2 - MINUSDI_LINE
      //--- Устанавливаем размер массива параметров и заполняем его
      ::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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- записываем описания буферов линий MAIN_LINE, PLUSDI_LINE и MINUSDI_LINE
      this.SetBufferDescription(MAIN_LINE,this.m_title);
      this.SetBufferDescription(PLUSDI_LINE,"+DI");
      this.SetBufferDescription(MINUSDI_LINE,"-DI");
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Alligator                                       |
//+------------------------------------------------------------------+
class CIndAlligator : public CIndMSTF
  {
public:
//--- Конструктор
   CIndAlligator(const string symbol,const ENUM_TIMEFRAMES timeframe,
                 const int jaw_period,                   // период для расчета челюстей
                 const int jaw_shift,                    // смещение челюстей по горизонтали
                 const int teeth_period,                 // период для расчета зубов
                 const int teeth_shift,                  // смещение зубов по горизонтали
                 const int lips_period,                  // период для расчета губ
                 const int lips_shift,                   // смещение губ по горизонтали
                 const ENUM_MA_METHOD ma_method,         // тип сглаживания
                 const ENUM_APPLIED_PRICE applied_price  // тип цены или handle
                ) : CIndMSTF(IND_ALLIGATOR,3,symbol,timeframe)
     {
      // Номера буферов: 0 - GATORJAW_LINE, 1 - GATORTEETH_LINE, 2 - GATORLIPS_LINE
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,8)==8)
        {
         ::ZeroMemory(this.m_param);
         //--- период для расчета челюстей
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(jaw_period<1 ? 13 : jaw_period);
         //--- смещение челюстей по горизонтали
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=jaw_shift;
         //--- период для расчета зубов
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(teeth_period<1 ? 8 : teeth_period);
         //--- смещение зубов по горизонтали
         this.m_param[3].type=TYPE_INT;
         this.m_param[3].integer_value=teeth_shift;
         //--- период для расчета губ
         this.m_param[4].type=TYPE_UINT;
         this.m_param[4].integer_value=(lips_period<1 ? 5 : lips_period);
         //--- смещение губ по горизонтали
         this.m_param[5].type=TYPE_INT;
         this.m_param[5].integer_value=lips_shift;
         //--- тип сглаживания
         this.m_param[6].type=TYPE_UINT;
         this.m_param[6].integer_value=ma_method;
         //--- тип цены или 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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
      this.SetParameters(param);
      this.SetName("Alligator");
      this.SetDescription("Alligator");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_WILLIAMS;
      //--- Записываем описания буферов линий GATORJAW_LINE, GATORTEETH_LINE и GATORLIPS_LINE
      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));
      //--- Записываем смещения в буферы GATORJAW_LINE, GATORTEETH_LINE и GATORLIPS_LINE
      this.SetBufferShift(GATORJAW_LINE,jaw_shift);
      this.SetBufferShift(GATORTEETH_LINE,teeth_shift);
      this.SetBufferShift(GATORLIPS_LINE,lips_shift);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Adaptive Moving Average                         |
//+------------------------------------------------------------------+
class CIndAMA : public CIndMSTF
  {
public:
//--- Конструктор
   CIndAMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int ama_period,                   // период AMA
           const int fast_ma_period,               // период быстрой скользящей
           const int slow_ma_period,               // период медленной скользящей
           const int ama_shift,                    // смещение индикатора по горизонтали
           const ENUM_APPLIED_PRICE applied_price  // тип цены или handle
          ) : CIndMSTF(IND_AMA,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,5)==5)
        {
         ::ZeroMemory(this.m_param);
         //--- период AMA
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ama_period<1 ? 9 : ama_period);
         //--- период быстрой скользящей
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(fast_ma_period<1 ? 2 : fast_ma_period);
         //--- период медленной скользящей
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(slow_ma_period<1 ? 30 : slow_ma_period);
         //--- смещение индикатора по горизонтали
         this.m_param[3].type=TYPE_INT;
         this.m_param[3].integer_value=ama_shift;
         //--- тип цены или 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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      //--- Записываем смещение в буфер 0
      this.SetBufferShift(0,ama_shift);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Awesome Oscillator                              |
//+------------------------------------------------------------------+
class CIndAO : public CIndMSTF
  {
public:
//--- Конструктор
   CIndAO(const string symbol,const ENUM_TIMEFRAMES timeframe) : CIndMSTF(IND_AO,1,symbol,timeframe)
     {
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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));
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Average True Range                              |
//+------------------------------------------------------------------+
class CIndATR : public CIndMSTF
  {
public:
//--- Конструктор
   CIndATR(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int ma_period                     // период усреднения
          ) : CIndMSTF(IND_ATR,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
      this.SetParameters(param);
      this.SetName("ATR");
      this.SetDescription("Average True Range");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Bears Power                                     |
//+------------------------------------------------------------------+
class CIndBears : public CIndMSTF
  {
public:
//--- Конструктор
   CIndBears(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int ma_period                      // период усреднения
            ) : CIndMSTF(IND_BEARS,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Bulls Power                                     |
//+------------------------------------------------------------------+
class CIndBulls : public CIndMSTF
  {
public:
//--- Конструктор
   CIndBulls(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int ma_period                      // период усреднения
            ) : CIndMSTF(IND_BULLS,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Bollinger Bands®                                |
//+------------------------------------------------------------------+
class CIndBands : public CIndMSTF
  {
public:
//--- Конструктор
   CIndBands(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int bands_period,                  // период для расчета средней линии
             const int bands_shift,                   // смещение индикатора по горизонтали
             const double deviation,                  // кол-во стандартных отклонений
             const ENUM_APPLIED_PRICE applied_price   // тип цены или handle
            ) : CIndMSTF(IND_BANDS,3,symbol,timeframe)
     {
      // Номера буферов: 0 - BASE_LINE, 1 - UPPER_BAND, 2 - LOWER_BAND
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- период для расчета средней линии
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(bands_period<1 ? 20 : bands_period);
         //--- смещение индикатора по горизонтали
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=bands_shift;
         //--- кол-во стандартных отклонений
         this.m_param[2].type=TYPE_DOUBLE;
         this.m_param[2].double_value=deviation;
         //--- тип цены или 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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //---Описание буферов линий BASE_LINE, UPPER_BAND и 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");
      //--- Записываем смещения в буферы BASE_LINE, UPPER_BAND и LOWER_BAND
      this.SetBufferShift(BASE_LINE,bands_shift);
      this.SetBufferShift(UPPER_BAND,bands_shift);
      this.SetBufferShift(LOWER_BAND,bands_shift);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Commodity Channel Index                         |
//+------------------------------------------------------------------+
class CIndCCI : public CIndMSTF
  {
public:
//--- Конструктор
   CIndCCI(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int ma_period,                    // период усреднения
           const ENUM_APPLIED_PRICE applied_price  // тип цены или handle
          ) : CIndMSTF(IND_CCI,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,2)==2)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period<2 ? 2 : ma_period);
         //--- тип цены или 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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Chaikin Oscillator                              |
//+------------------------------------------------------------------+
class CIndCHO : public CIndMSTF
  {
public:
//--- Конструктор
   CIndCHO(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int fast_ma_period,                  // быстрый период
           const int slow_ma_period,                  // медленный период
           const ENUM_MA_METHOD ma_method,            // тип сглаживания
           const ENUM_APPLIED_VOLUME applied_volume   // используемый объем
          ) : CIndMSTF(IND_CHAIKIN,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- быстрый период
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(fast_ma_period<1 ? 3 : fast_ma_period);
         //--- медленный период
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(slow_ma_period<1 ? 10 : slow_ma_period);
         //--- тип сглаживания
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=ma_method;
         //--- используемый объем
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=applied_volume;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Double Exponential Moving Average               |
//+------------------------------------------------------------------+
class CIndDEMA : public CIndMSTF
  {
public:
//--- Конструктор
   CIndDEMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
            const int ma_period,                      // период усреднения
            const int ma_shift,                       // смещение индикатора по горизонтали
            const ENUM_APPLIED_PRICE applied_price    // тип цены или handle
          ) : CIndMSTF(IND_DEMA,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,3)==3)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
         //--- смещение индикатора по горизонтали
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=ma_shift;
         //--- тип цены или 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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      //--- Записываем смещение в буфер 0
      this.SetBufferShift(0,ma_shift);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора DeMarker                                        |
//+------------------------------------------------------------------+
class CIndDeM : public CIndMSTF
  {
public:
//--- Конструктор
   CIndDeM(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int ma_period                     // период усреднения
          ) : CIndMSTF(IND_DEMARKER,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Envelopes                                       |
//+------------------------------------------------------------------+
class CIndEnvelopes : public CIndMSTF
  {
public:
//--- Конструктор
   CIndEnvelopes(const string symbol,const ENUM_TIMEFRAMES timeframe,
                 const int ma_period,                    // период для расчета средней линии
                 const int ma_shift,                     // смещение индикатора по горизонтали
                 const ENUM_MA_METHOD ma_method,         // тип сглаживания
                 const ENUM_APPLIED_PRICE applied_price, // тип цены или handle
                 const double deviation                  // отклонение границ от средней линии
          ) : CIndMSTF(IND_ENVELOPES,2,symbol,timeframe)
     {
      // Номера буферов: 0 - UPPER_LINE, 1 - LOWER_LINE
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,5)==5)
        {
         ::ZeroMemory(this.m_param);
         //--- период для расчета средней линии
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
         //--- смещение индикатора по горизонтали
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=ma_shift;
         //--- тип сглаживания
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=ma_method;
         //--- тип цены или handle
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=applied_price;
         //--- отклонение границ от средней линии
         this.m_param[4].type=TYPE_UINT;
         this.m_param[4].double_value=deviation;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Описание буферов линий UPPER_LINE и LOWER_LINE
      this.SetBufferDescription(UPPER_LINE,this.m_title+" Upper");
      this.SetBufferDescription(LOWER_LINE,this.m_title+" Lower");
      //--- Записываем смещение в буфер 0
      this.SetBufferShift(0,ma_shift);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Force Index                                     |
//+------------------------------------------------------------------+
class CIndForce : public CIndMSTF
  {
public:
//--- Конструктор
   CIndForce(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int                 ma_period,     // период усреднения
             const ENUM_MA_METHOD      ma_method,     // тип сглаживания
             const ENUM_APPLIED_VOLUME applied_volume // тип объема для расчета
            ) : CIndMSTF(IND_FORCE,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,3)==3)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 13 : ma_period);
         //--- тип сглаживания
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=ma_method;
         //--- тип объема для расчета
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=applied_volume;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Fractals                                        |
//+------------------------------------------------------------------+
class CIndFractals : public CIndMSTF
  {
public:
//--- Конструктор
   CIndFractals(const string symbol,const ENUM_TIMEFRAMES timeframe) : CIndMSTF(IND_FRACTALS,2,symbol,timeframe)
     {
      // Номера буферов: 0 - UPPER_LINE, 1 - LOWER_LINE
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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));
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
      this.SetParameters(param);
      this.SetName("Fractals");
      this.SetDescription("Fractals");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_WILLIAMS;
      //--- Описание буферов линий UPPER_LINE и LOWER_LINE
      this.SetBufferDescription(UPPER_LINE,this.m_title+" Up");
      this.SetBufferDescription(LOWER_LINE,this.m_title+" Down");
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Fractal Adaptive Moving Average                 |
//+------------------------------------------------------------------+
class CIndFrAMA : public CIndMSTF
  {
public:
//--- Конструктор
   CIndFrAMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int ma_period,                     // период усреднения
             const int ma_shift,                      // смещение индикатора по горизонтали
             const ENUM_APPLIED_PRICE applied_price   // тип цены или handle
            ) : CIndMSTF(IND_FRAMA,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,3)==3)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
         //--- смещение индикатора по горизонтали
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=ma_shift;
         //--- тип цены или 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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      //--- Записываем смещение в буфер 0
      this.SetBufferShift(0,ma_shift);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Gator Oscillator                                |
//+------------------------------------------------------------------+
class CIndGator : public CIndMSTF
  {
public:
//--- Конструктор
   CIndGator(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int jaw_period,                    // период для расчета челюстей
             const int jaw_shift,                     // смещение челюстей по горизонтали
             const int teeth_period,                  // период для расчета зубов
             const int teeth_shift,                   // смещение зубов по горизонтали
             const int lips_period,                   // период для расчета губ
             const int lips_shift,                    // смещение губ по горизонтали
             const ENUM_MA_METHOD ma_method,          // тип сглаживания
             const ENUM_APPLIED_PRICE applied_price   // тип цены или handle
            ) : CIndMSTF(IND_GATOR,4,symbol,timeframe)
     {
      // Номера буферов: 0 - UPPER_HISTOGRAM, 1- цветовой буфер верхней гистограммы, 2 - LOWER_HISTOGRAM, 3- цветовой буфер нижней гистограммы
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,8)==8)
        {
         ::ZeroMemory(this.m_param);
         //--- период для расчета челюстей
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(jaw_period<1 ? 13 : jaw_period);
         //--- смещение челюстей по горизонтали
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=jaw_shift;
         //--- период для расчета зубов
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(teeth_period<1 ? 8 : teeth_period);
         //--- смещение зубов по горизонтали
         this.m_param[3].type=TYPE_INT;
         this.m_param[3].integer_value=teeth_shift;
         //--- период для расчета губ
         this.m_param[4].type=TYPE_UINT;
         this.m_param[4].integer_value=(lips_period<1 ? 5 : lips_period);
         //--- смещение губ по горизонтали
         this.m_param[5].type=TYPE_INT;
         this.m_param[5].integer_value=lips_shift;
         //--- тип сглаживания
         this.m_param[6].type=TYPE_UINT;
         this.m_param[6].integer_value=ma_method;
         //--- тип цены или 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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Описание буферов линий UPPER_HISTOGRAM, буфер цвета верхней гистограммы, LOWER_HISTOGRAM и буфер цвета нижней гистограммы
      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                              |
//+------------------------------------------------------------------+
class CIndIchimoku : public CIndMSTF
  {
public:
//--- Конструктор
   CIndIchimoku(const string symbol,const ENUM_TIMEFRAMES timeframe,
                const int tenkan_sen,                    // период Tenkan-sen
                const int kijun_sen,                     // период Kijun-sen
                const int senkou_span_b                  // период Senkou Span B
               ) : CIndMSTF(IND_ICHIMOKU,5,symbol,timeframe)
     {
      // Номера буферов: 0 - TENKANSEN_LINE, 1 - KIJUNSEN_LINE, 2 - SENKOUSPANA_LINE, 3 - SENKOUSPANB_LINE, 4 - CHIKOUSPAN_LINE
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,3)==3)
        {
         ::ZeroMemory(this.m_param);
         //--- период Tenkan-sen
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(tenkan_sen<1 ? 9 : tenkan_sen);
         //--- период Kijun-sen
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(kijun_sen<1 ? 26 : kijun_sen);
         //--- период 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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Описание буферов линий TENKANSEN_LINE, KIJUNSEN_LINE, SENKOUSPANA_LINE, SENKOUSPANB_LINE и 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");
      //--- Записываем смещения в буферы SENKOUSPANA_LINE, SENKOUSPANB_LINE и 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                       |
//+------------------------------------------------------------------+
class CIndBWMFI : public CIndMSTF
  {
public:
//--- Конструктор
   CIndBWMFI(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const ENUM_APPLIED_VOLUME applied_volume // тип объема для расчета
            ) : CIndMSTF(IND_BWMFI,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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));
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
      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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Momentum                                        |
//+------------------------------------------------------------------+
class CIndMomentum : public CIndMSTF
  {
public:
//--- Конструктор
   CIndMomentum(const string symbol,const ENUM_TIMEFRAMES timeframe,
                const int                 mom_period,    // период усреднения
                const ENUM_APPLIED_PRICE  applied_price  // тип цены или handle
               ) : CIndMSTF(IND_MOMENTUM,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,2)==2)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(mom_period<1 ? 14 : mom_period);
         //--- тип цены или 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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Money Flow Index                                |
//+------------------------------------------------------------------+
class CIndMFI : public CIndMSTF
  {
public:
//--- Конструктор
   CIndMFI(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int                 ma_period,       // период усреднения
           const ENUM_APPLIED_VOLUME applied_volume   // тип объема для расчета
          ) : CIndMSTF(IND_MFI,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,2)==2)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
         //--- тип объема для расчета
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=applied_volume;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
      this.SetParameters(param);
      this.SetName("MFI");
      this.SetDescription("Money Flow Index");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_VOLUME;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Moving Average                                  |
//+------------------------------------------------------------------+
class CIndMA : public CIndMSTF
  {
public:
//--- Конструктор
   CIndMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
          const int                 ma_period,     // период усреднения
          const int                 ma_shift,      // смещение индикатора по горизонтали
          const ENUM_MA_METHOD      ma_method,     // тип сглаживания
          const ENUM_APPLIED_PRICE  applied_price  // тип цены или handle
         ) : CIndMSTF(IND_MA,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 10 : ma_period);
         //--- смещение индикатора по горизонтали
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=ma_shift;
         //--- тип сглаживания
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=ma_method;
         //--- тип цены или 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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      //--- Записываем смещение в буфер 0
      this.SetBufferShift(0,ma_shift);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Moving Average of Oscillator                    |
//+------------------------------------------------------------------+
class CIndOsMA : public CIndMSTF
  {
public:
//--- Конструктор
   CIndOsMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
            const int                fast_ema_period, // период быстрой средней
            const int                slow_ema_period, // период медленной средней
            const int                signal_period,   // период усреднения разности
            const ENUM_APPLIED_PRICE applied_price    // тип цены или handle
           ) : CIndMSTF(IND_OSMA,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- период быстрой средней
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(fast_ema_period<1 ? 12 : fast_ema_period);
         //--- период медленной средней
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(slow_ema_period<1 ? 26 : slow_ema_period);
         //--- период усреднения разности
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(signal_period<1 ? 9 : signal_period<2 ? 2 : signal_period);
         //--- тип цены или 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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Moving Averages Convergence/Divergence          |
//+------------------------------------------------------------------+
class CIndMACD : public CIndMSTF
  {
public:
//--- Конструктор
   CIndMACD(const string symbol,const ENUM_TIMEFRAMES timeframe,
            const int                fast_ema_period, // период быстрой средней
            const int                slow_ema_period, // период медленной средней
            const int                signal_period,   // период усреднения разности
            const ENUM_APPLIED_PRICE applied_price    // тип цены или handle
           ) : CIndMSTF(IND_MACD,2,symbol,timeframe)
     {
      // Номера буферов: 0 - MAIN_LINE, 1 - SIGNAL_LINE
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- период быстрой средней
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(fast_ema_period<1 ? 12 : fast_ema_period);
         //--- период медленной средней
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(slow_ema_period<1 ? 26 : slow_ema_period);
         //--- период усреднения разности
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(signal_period<1 ? 9 : signal_period);
         //--- тип цены или 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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Описание буферов линии MAIN_LINE и SIGNAL_LINE
      this.SetBufferDescription(MAIN_LINE,this.m_title);
      this.SetBufferDescription(SIGNAL_LINE,"Signal");
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора On Balance Volume                               |
//+------------------------------------------------------------------+
class CIndOBV : public CIndMSTF
  {
public:
//--- Конструктор
   CIndOBV(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const ENUM_APPLIED_VOLUME applied_volume   // тип объема для расчета
          ) : CIndMSTF(IND_OBV,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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));
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Parabolic Stop and Reverse system               |
//+------------------------------------------------------------------+
class CIndSAR : public CIndMSTF
  {
public:
//--- Конструктор
   CIndSAR(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const double step,                      // шаг изменения цены - коэффициент ускорения
           const double maximum                    // максимальный шаг
          ) : CIndMSTF(IND_SAR,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,2)==2)
        {
         ::ZeroMemory(this.m_param);
         //--- шаг изменения цены - коэффициент ускорения
         this.m_param[0].type=TYPE_DOUBLE;
         this.m_param[0].double_value=step;
         //--- максимальный шаг
         this.m_param[1].type=TYPE_DOUBLE;
         this.m_param[1].double_value=maximum;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
      this.SetParameters(param);
      this.SetName("SAR");
      this.SetDescription("Parabolic SAR");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Relative Strength Index                         |
//+------------------------------------------------------------------+
class CIndRSI : public CIndMSTF
  {
public:
//--- Конструктор
   CIndRSI(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int                ma_period,     // период усреднения
           const ENUM_APPLIED_PRICE applied_price  // тип цены или handle
          ) : CIndMSTF(IND_RSI,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,2)==2)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period<2 ? 2 : ma_period);
         //--- тип цены или 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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Relative Vigor Index                            |
//+------------------------------------------------------------------+
class CIndRVI : public CIndMSTF
  {
public:
//--- Конструктор
   CIndRVI(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int ma_period                     // период усреднения
          ) : CIndMSTF(IND_RVI,2,symbol,timeframe)
     {
      // Номера буферов: 0 - MAIN_LINE, 1 - SIGNAL_LINE
      //--- Устанавливаем размер массива параметров и заполняем его
      ::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 ? 10 : ma_period);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Описание буферов линии MAIN_LINE и SIGNAL_LINE
      this.SetBufferDescription(MAIN_LINE,this.m_title);
      this.SetBufferDescription(SIGNAL_LINE,"Signal");
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Standard Deviation                              |
//+------------------------------------------------------------------+
class CIndStdDev : public CIndMSTF
  {
public:
//--- Конструктор
   CIndStdDev(const string symbol,const ENUM_TIMEFRAMES timeframe,
              const int                ma_period,        // период усреднения
              const int                ma_shift,         // смещение индикатора по горизонтали
              const ENUM_MA_METHOD     ma_method,        // тип сглаживания
              const ENUM_APPLIED_PRICE applied_price     // тип цены или handle
             ) : CIndMSTF(IND_STDDEV,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 20 : ma_period<2 ? 2 : ma_period);
         //--- смещение индикатора по горизонтали
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=ma_shift;
         //--- тип сглаживания
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=ma_method;
         //--- тип цены или 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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      //--- Записываем смещение в буфер 0
      this.SetBufferShift(0,ma_shift);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Stochastic Oscillator                           |
//+------------------------------------------------------------------+
class CIndStoch : public CIndMSTF
  {
public:
//--- Конструктор
   CIndStoch(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int              Kperiod,          // K-период (количество баров для расчетов)
             const int              Dperiod,          // D-период (период первичного сглаживания)
             const int              slowing,          // окончательное сглаживание
             const ENUM_MA_METHOD   ma_method,        // тип сглаживания
             const ENUM_STO_PRICE   price_field       // способ расчета стохастика
            ) : CIndMSTF(IND_STOCHASTIC,2,symbol,timeframe)
     {
      // Номера буферов: 0 - MAIN_LINE, 1 - SIGNAL_LINE
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,5)==5)
        {
         ::ZeroMemory(this.m_param);
         //--- K-период (количество баров для расчетов)
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(Kperiod<1 ? 5 : Kperiod);
         //--- D-период (период первичного сглаживания)
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(Dperiod<1 ? 3 : Dperiod);
         //--- окончательное сглаживание
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(slowing<1 ? 3 : slowing);
         //--- тип сглаживания
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=ma_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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Описание буферов линии MAIN_LINE и SIGNAL_LINE
      this.SetBufferDescription(MAIN_LINE,this.m_title);
      this.SetBufferDescription(SIGNAL_LINE,"Signal");
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Triple Exponential Moving Average               |
//+------------------------------------------------------------------+
class CIndTEMA : public CIndMSTF
  {
public:
//--- Конструктор
   CIndTEMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
            const int                  ma_period,     // период усреднения
            const int                  ma_shift,      // смещение индикатора по горизонтали
            const ENUM_APPLIED_PRICE   applied_price  // тип цены или handle
           ) : CIndMSTF(IND_TEMA,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,3)==3)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
         //--- смещение индикатора по горизонтали
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=ma_shift;
         //--- тип цены или 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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      //--- Записываем смещение в буфер 0
      this.SetBufferShift(0,ma_shift);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Triple Exponential Moving Averages Oscillator   |
//+------------------------------------------------------------------+
class CIndTriX : public CIndMSTF
  {
public:
//--- Конструктор
   CIndTriX(const string symbol,const ENUM_TIMEFRAMES timeframe,
            const int                ma_period,       // период усреднения
            const ENUM_APPLIED_PRICE applied_price    // тип цены или handle
           ) : CIndMSTF(IND_TRIX,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,2)==2)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period<2 ? 2 : ma_period);
         //--- тип цены или 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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
      this.SetParameters(param);
      this.SetName("TRIX");
      this.SetDescription("Triple Exponential Average");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Larry Williams' Percent Range                   |
//+------------------------------------------------------------------+
class CIndWPR : public CIndMSTF
  {
public:
//--- Конструктор
   CIndWPR(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int calc_period                   // период усреднения
          ) : CIndMSTF(IND_WPR,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::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=(calc_period<1 ? 14 : calc_period);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Variable Index Dynamic Average                  |
//+------------------------------------------------------------------+
class CIndVIDyA : public CIndMSTF
  {
public:
//--- Конструктор
   CIndVIDyA(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int                 cmo_period,    // период Chande Momentum
             const int                 ema_period,    // период фактора сглаживания
             const int                 ma_shift,      // смещение индикатора по горизонтали
             const ENUM_APPLIED_PRICE  applied_price  // тип цены или handle
            ) : CIndMSTF(IND_VIDYA,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- период Chande Momentum
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(cmo_period<1 ? 9 : cmo_period);
         //--- период фактора сглаживания
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(ema_period<1 ? 12 : ema_period);
         //--- смещение индикатора по горизонтали
         this.m_param[2].type=TYPE_INT;
         this.m_param[2].integer_value=ma_shift;
         //--- тип цены или 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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      //--- Записываем смещение в буфер 0
      this.SetBufferShift(0,ma_shift);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Volumes                                         |
//+------------------------------------------------------------------+
class CIndVolumes : public CIndMSTF
  {
public:
//--- Конструктор
   CIndVolumes(const string symbol,const ENUM_TIMEFRAMES timeframe,
               const ENUM_APPLIED_VOLUME applied_volume  // тип объема
              ) : CIndMSTF(IND_VOLUMES,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::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());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      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));
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и 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;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
     }
  };
//+------------------------------------------------------------------+
//| Класс пользовательского индикатора                               |
//+------------------------------------------------------------------+
class CIndCustom : public CIndMSTF
  {
public:
//--- Конструктор
   CIndCustom(const string symbol,const ENUM_TIMEFRAMES timeframe,
              const string path,                      // путь к индикатору (например, "Examples\\MACD.ex5")
              const string name,                      // имя пользовательского индикатора
              const uint   buffers,                   // количество буферов индикатора
              const MqlParam &param[]                 // массив параметров пользовательского индикатора
             ) : CIndMSTF(IND_CUSTOM,buffers,symbol,timeframe)
     {
      //--- Если передан пустой массив параметров - сообщаем об этом в журнал
      int total=(int)param.Size();
      if(total==0)
         ::PrintFormat("%s Error. Passed an empty array",__FUNCTION__);
      //--- Если массив не пустой и его размер увеличен на 1 (в первый параметр типа string должно быть записано имя индикатора)
      ResetLastError();
      if(total>0 && ::ArrayResize(this.m_param,total+1)==total+1)
        {
         //--- Обнуляем данные в массиве и вписываем имя (путь к файлу и имя .ex5 файла)
         ::ZeroMemory(this.m_param);
         //--- имя пользовательского индикатора
         this.m_param[0].type=TYPE_STRING;
         this.m_param[0].string_value=path;
         //--- заполняем массив параметров индикатора
         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;
           }
         //--- Создаём описание параметров.
         //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
         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));
         //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
         this.SetParameters(param);
         this.SetName(name);
         this.SetDescription(name);
         this.m_title=this.Name()+this.Parameters();
         this.m_category=IND_CATEGORY_CUSTOM;
         //--- Записываем описание первого буфера линии
         this.SetBufferDescription(0,this.m_title);
        }
     }
  };

A lista apresentada contém todas as classes para a criação de todos os indicadores técnicos disponíveis no terminal do cliente + uma classe para a criação de um indicador personalizado. Todas as classes são idênticas entre si e os pontos principais são comentados no código das classes.

A criação e o funcionamento de todos os indicadores apresentados acima não foram testados. Foram testados apenas os indicadores baseados em médias móveis, que desenham uma única linha no gráfico principal. Nos próximos artigos, escreveremos templates para a criação de versões com vários símbolos/períodos de todos os tipos de indicadores técnicos.

Essencialmente, tudo já está pronto para a criação de multi-indicadores. Mas iremos um pouco além e criaremos uma ferramenta prática para automatizar a criação e o uso de indicadores com vários símbolos/períodos. Será uma classe-coleção de indicadores, permitindo a criação fácil de indicadores padrão ou personalizados, transformando-os em indicadores com vários símbolos/períodos.


Classe coleção de indicadores

A classe-coleção de indicadores é essencialmente uma lista comum de ponteiros para objetos. Simplesmente adicionaremos a ela métodos para a fácil criação de indicadores e a adição dos recém-criados à coleção. Também será possível obter facilmente um ponteiro para o indicador desejado e recuperar os dados necessários dele. Ao adicionar um novo indicador à coleção, primeiro será verificada a presença de um indicador exatamente igual na coleção. Se ele estiver presente, o novo objeto criado é deletado e retorna-se um ponteiro para o já existente na coleção,para que dois objetos diferentes não se refiram à mesma parte de cálculo.

A estrutura da classe-coleção seguirá a estrutura da classe base do indicador com vários símbolos/períodos. Com a única diferença de que a maioria dos métodos deve primeiro encontrar o indicador desejado pelo seu handle e, em seguida, chamar o método necessário para definir ou obter um valor ou o resultado das ações.

No mesmo arquivo \MQL5\Include\IndMSTF\IndMSTF.mqh, simplesmente continuaremos a escrever uma nova classe. Para que isso funcione, é necessário incluir o arquivo da classe de array dinâmico de ponteiros para instâncias da classe CObject e seus descendentes CArrayObj:

//+------------------------------------------------------------------+
//| Класс-коллекция индикаторов                                      |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
class CMSTFIndicators
  {
private:

public:
  
  }

Inseriremos no corpo da classe o objeto da lista e métodos para trabalhar com a classe:

//+------------------------------------------------------------------+
//| Класс-коллекция индикаторов                                      |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
class CMSTFIndicators
  {
private:
   CArrayObj         m_list;
//--- Создаёт индикатор для переданного объекта
   bool              CreateIndicator(CIndMSTF *ind_obj);
//--- Добавляет указанный индикатор в коллекцию
   int               AddNewIndicator(CIndMSTF *ind_obj,const string source);

public:
//--- Возвращает (1) объект индикатора по хэндлу, (2) количество индикаторов в коллекции
   CIndMSTF         *GetIndicatorObj(const int ind_handle,const string source) const;
   uint              IndicatorsTotal(void)                  const { return this.m_list.Total(); }

//--- Заполняет данными буферы (1) индикатора по хэндлу, (2) всех индикаторов в коллекции
   bool              Calculate(const int ind_handle);
   bool              Calculate(void);
//--- Устанавливает (1) указанное, (2) по умолчанию описание линии буфера индикатора
   void              SetPlotLabel(const uint plot_index,const string descript);
   void              SetPlotLabelFromBuffer(const uint plot_index,const int ind_handle,const uint buffer_num);
//--- Устанавливает смещение указанному рисуемому буферу
   void              SetPlotShift(const uint plot_index,const int shift);
//--- (1) Устанавливает (2) возвращает инициализирующее значение указанного буфера указанного по хэндлу индикатора
   void              SetBufferInitValue(const int ind_handle,const uint buffer_num,const double value);
   double            BufferInitValue(const int ind_handle,const uint buffer_num) const;

//--- Возвращает данные индикатора по хэндлу из указанного буфера по индексу (1) как есть, (2) на указанный символ/таймфрейм
   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) Копирует данные указанного буфера расчётной части указанного по хэндлу индикатора в буфер индикатора с учётом символа/периода графика,
//--- (2) возвращает количество данных в укакзанном буфере указанного по хэндлу индикатора
   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;

   //--- Возвращает (1) описание буфера, (2) состояние данных линии указанного буфера указанного по хэндлу индикатора на указанном баре
   //--- (3) состояние линии индикатора с учётом символа/периода графика, (4) состояние отношения линии индикатора с указанным уровнем,
   //--- (5)  состояние отношения линии индикатора с указанным уровнем с учётом символа/периода графика, (6) описание категории индикатора
   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);
   
//--- Устанавливает (1) идентификатор, (2) Digits, (3) пользовательское описание, (4) описание буфера
   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);

//--- Возвращает флаг серийности указанного буфера, (2) синхронизированности исторических данных по символу/периоду
   bool              IsSeries(const int ind_handle,const uint buffer_num) const;
   bool              IsSynchronized(const int ind_handle) const;
   
//--- Возвращает (1) таймфрейм, (2) символ, (3) наименование, (4) список параметров, (5) хэндл, (6) Digits
//--- количество (7) буферов, (8) баров, (9) идентификатор, (10) описание, (11) заголовок, (12) категорию,
//--- (13) количество параметрпов, (14) тип программы, описание (15) категории, (16) буфера индикатора
   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;
//--- Возвращает (1) структуру параметров по индексу из массива, (2) описание таймфрейма
   MqlParam          GetMqlParam(const int ind_handle,const int index) const;
   string            TimeframeDescription(const int ind_handle)    const;
//--- Возвращает количество рассчитанных данных
   int               Calculated(const int ind_handle) const;
   
//--- Виртуальный метод, возвращающий тип объекта (индикатора)
      ENUM_INDICATOR    Type(const int ind_handle) const;
   
//--- Методы добавления индикаторов в коллекцию
   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,       // путь к индикатору (например, "Examples\\MACD.ex5")
                                                                                      const string name,       // имя пользовательского индикатора (например, "Custom MACD")
                                                                                      const uint   buffers,    // количество буферов
                                                                                      const MqlParam &param[]);// Массив параметров
//--- Таймер
   void OnTimer(void)
     {
      //--- В цикле по всем индикаторам коллекции
      int total=this.m_list.Total();
      for(int i=0;i<total;i++)
        {
         //--- получаем указатель на очередной объект-индикатор
         //--- и вызываем его таймер
         CIndMSTF *obj=this.m_list.At(i);
         if(obj!=NULL)
            obj.OnTimer();
        }
     }
//--- Конструктор/Деструктор
                     CMSTFIndicators(void){ this.m_list.Clear(); }
                    ~CMSTFIndicators(void){;}
  };


Implementação de métodos para trabalhar com indicadores na lista:

//+------------------------------------------------------------------+
//| Создаёт расчётную часть индикатора для переданного объекта       |
//+------------------------------------------------------------------+
bool CMSTFIndicators::CreateIndicator(CIndMSTF *ind_obj)
  {
   //--- Если расчётную часть индикатора создать не удалось
   if(!ind_obj.CreateIndicator())
     {
      //--- ищем индекс объекта-индикатора в списке коллекции
      //--- и по полученному индексу удаляем объект-индикатор из списка коллекции
      this.m_list.Sort();
      int index=this.m_list.Search(ind_obj);
      this.m_list.Delete(index);
      //--- Возвращаем false
      return false;
     }
//--- Расчётная часть успешно создана - возвращаем true
   return true;
  }
//+------------------------------------------------------------------+
//| Возвращает объект индикатора по хэндлу расчётной части           |
//+------------------------------------------------------------------+
CIndMSTF *CMSTFIndicators::GetIndicatorObj(const int ind_handle,const string source) const
  {
//--- Если в метод передан невалидный хэндл - сообщаем об это и возвращаем NULL
   if(ind_handle==INVALID_HANDLE)
     {
      ::PrintFormat("%s: Error handle",source);
      return NULL;
     }
//--- В цикле по всем объектам-индикаторам в списке коллекции
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      //--- получаем указатель на очередной объект-индикатор
      CIndMSTF *obj=this.m_list.At(i);
      if(obj==NULL)
         continue;
      //--- Если хэндл индикатора равен переданному в метод -
      //--- возвращаем указатель на найденный объект-индикатор
      if(obj.Handle()==ind_handle)
         return obj;
     }
//--- Ничего не нашли - возвращаем NULL
   return NULL;
  }
//+------------------------------------------------------------------+
//| Заполняет данными буферы индикатора по хэндлу                    |
//+------------------------------------------------------------------+
bool CMSTFIndicators::Calculate(const int ind_handle)
  {
   //--- Получаем по хэндлу указатель на объект-индикатор
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
      return false;
   //--- Возвращаем результат работы метода Calculate полученн6ого по хэндлу объекта-индикатора
   return obj.Calculate();
  }
//+------------------------------------------------------------------+
//| Заполняет данными буферы всех индикаторов в коллекции            |
//+------------------------------------------------------------------+
bool CMSTFIndicators::Calculate(void)
  {
   //--- Объявляем переменную для хранения результата
   bool res=true;
//--- В цикле по всем объектам-индикаторам в списке коллекции
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      //--- получаем указатель на очередной объект-индикатор
      CIndMSTF *obj=this.m_list.At(i);
      if(obj==NULL)
         continue;
      //--- Добавляем к переменной res результат вызова метода Calculate очередного объекта-индикатора
      res &=obj.Calculate();
      //--- Если метод отработал с ошибкой - сообщаем об этом в журнал
      if(!res)
         ::PrintFormat("%s::%s: Error in indicator calculation: %s",__FUNCTION__,obj.Title(),TypeErrorcDescription(obj.TypeError()));
     }
//--- Если общий результат false - сообщаем об этом в журнал
   if(!res)
      ::PrintFormat("%s: Not all indicators have been calculated successfully. It is necessary to recalculate the buffers of all indicators",__FUNCTION__);
//--- Возвращаем результат вызова методов Calculate всех индикаторов в коллекции
   return res;
  }
//+------------------------------------------------------------------+
//| Возвращает данные индикатора по хэндлу                           |
//| из указанного буфера по индексу как есть                         |
//+------------------------------------------------------------------+
double CMSTFIndicators::GetData(const int ind_handle,const uint buffer_num,const uint index)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return EMPTY_VALUE;
     }
//--- Возвращаем данные из указанного буфера индикатора по индексу, переданному в метод
   return obj.GetData(buffer_num,index);
  }
//+------------------------------------------------------------------+
//| Возвращает данные индикатора по хэндлу                           |
//| из указанного буфера по индексу на указанный символ/таймфрейм    |
//+------------------------------------------------------------------+
double CMSTFIndicators::GetDataTo(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const int ind_handle,const uint buffer_num,const uint index)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return EMPTY_VALUE;
     }
//--- Возвращаем данные из указанного буфера индикатора по индексу, переданному в метод
   return obj.GetDataTo(symbol_to,timeframe_to,buffer_num,index);
  }
//+------------------------------------------------------------------+
//| Заполняет переданный буфер индикатора данными                    |
//+------------------------------------------------------------------+
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[])
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return false;
     }
//--- Заполняем переданный в метод массив-буфер из указанного буфера индикатора
   return obj.DataToBuffer(symbol_to,timeframe_to,buffer_num,limit,buffer);
  }
//+------------------------------------------------------------------+
//| Устанавливает указанное описание для линии буфера                |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetPlotLabel(const uint plot_index,const string descript)
  {
   ::PlotIndexSetString(plot_index,PLOT_LABEL,descript);
  }
//+------------------------------------------------------------------+
//| Устанавливает описание по умолчанию линии буфера                 |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetPlotLabelFromBuffer(const uint plot_index,const int ind_handle,const uint buffer_num)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return;
     }
//--- Устанавливаем в указанный рисуемый буфер описание указанного буфера индикатора
   ::PlotIndexSetString(plot_index,PLOT_LABEL,obj.BufferDescription(buffer_num));
  }
//+------------------------------------------------------------------+
//| Устанавливает смещение указанному рисуемому буферу               |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetPlotShift(const uint plot_index,const int shift)
  {
   ::PlotIndexSetInteger(plot_index,PLOT_SHIFT,shift);
  }
//+------------------------------------------------------------------+
//| Возвращает описание указанного буфера                            |
//| указанного по хэндлу индикатора                                  |
//+------------------------------------------------------------------+
string CMSTFIndicators::BufferDescription(const int ind_handle,const uint buffer_num)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
//--- Если указатель на объект получен - возвращаем из него описание указанного буфера. Иначе - текст ошибки
   return(obj!=NULL ? obj.BufferDescription(buffer_num) : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__));
  }
//+------------------------------------------------------------------+
//| Устанавливает инициализирующее значение указанного буфера        |
//| указанного по хэндлу индикатора                                  |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetBufferInitValue(const int ind_handle,const uint buffer_num,const double value)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return;
     }
//--- Устанавливаем для указанного буфера указанное инициализирующее "пустое" значение
   obj.SetBufferInitValue(buffer_num,value);
  }
//+------------------------------------------------------------------+
//| Возвращает инициализирующее значение указанного буфера           |
//| указанного по хэндлу индикатора                                  |
//+------------------------------------------------------------------+
double CMSTFIndicators::BufferInitValue(const int ind_handle,const uint buffer_num) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return WRONG_VALUE;
     }
//--- Возвращаем инициализирующее "пустое" значение, установленное для указанного буфера
   return obj.BufferInitValue(buffer_num);
  }
//+------------------------------------------------------------------+
//| Возвращает состояние данных линии указанного буфера              |
//| указанного по хэндлу индикатора на указанном баре                |
//+------------------------------------------------------------------+
ENUM_LINE_STATE CMSTFIndicators::BufferLineState(const int ind_handle,const uint buffer_num,const int index)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return LINE_STATE_NONE;
     }
//--- Возвращаем состояние линии указанного буфера на указанном индексе
   return obj.BufferLineState(buffer_num,index);
  }
//+------------------------------------------------------------------+
//| Возвращает состояние данных линии указанного буфера              |
//| указанного по хэндлу индикатора на баре символа/таймфрейма       |
//+------------------------------------------------------------------+
ENUM_LINE_STATE CMSTFIndicators::BufferLineState(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ind_handle,const uint buffer_num,const int index)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return LINE_STATE_NONE;
     }
//--- Получаем время бара, переданного в метод
   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;
     }
//--- Получаем номер бара в буфере объекта-индикатора, соответствующий найденному времени
   int bar=::iBarShift(obj.Symbol(),obj.Timeframe(),array[0]);
//--- Если бар получен - возвращаем состояние линии на найденном баре, иначе - неопределённое состояние
   return(bar!=WRONG_VALUE ? obj.BufferLineState(buffer_num,bar) : LINE_STATE_NONE);
  }
//+------------------------------------------------------------------+
//| Возвращает соотношение данных линии указанного буфера            |
//| указанного по хэндлу индикатора на указанном баре                |
//| с указанными значениями                                          |
//+------------------------------------------------------------------+
ENUM_LINE_STATE CMSTFIndicators::BufferLineStateRelative(const int ind_handle,const int buffer_num,const int index,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return LINE_STATE_NONE;
     }
//--- Возвращаем соотношение линии индикатора и уровня в указанном буфере на указанном индексе
   return obj.BufferLineStateRelative(buffer_num,index,level0,level1);
  }
//+------------------------------------------------------------------+
//| Возвращает соотношение данных линии указанного буфера            |
//| указанного по хэндлу индикатора на указанном баре                |
//| с указанными значениями на указанном символе/периоде графика     |
//+------------------------------------------------------------------+
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)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return LINE_STATE_NONE;
     }
//--- Возвращаем соотношение линии индикатора и уровня в указанном буфере на указанном индексе
   return obj.BufferLineStateRelative(symbol,timeframe,buffer_num,index,level0,level1);
  }
//+------------------------------------------------------------------+
//| Возвращает описание категории                                    |
//+------------------------------------------------------------------+
string CMSTFIndicators::CategoryDescription(const int ind_handle)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
//--- Если объект получен, возвращаем описание категории. Иначе - текст ошибки
   return(obj!=NULL ? obj.CategoryDescription() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__));
  }
//+------------------------------------------------------------------+
//| Устанавливает идентификатор                                      |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetID(const int ind_handle,const int id)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return;
     }
//--- Устанавливаем идентификатор для полученного объекта
   obj.SetID(id);
  }
//+------------------------------------------------------------------+
//| Устанавливает Digits индикатора                                  |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetDigits(const int ind_handle,const int digits)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return;
     }
//--- Устанавливаем Digits для полученного объекта
   obj.SetDigits(digits);
  }
//+------------------------------------------------------------------+
//| Устанавливает пользовательское описание                          |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetDescription(const int ind_handle,const string descr)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return;
     }
//--- Устанавливаем описание для полученного объекта
   obj.SetDescription(descr);
  }
//+------------------------------------------------------------------+
//| Устанавливает описание указанного буфера                         |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetBufferDescription(const int ind_handle,const uint buffer_num,const string descr)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return;
     }
//--- Устанавливаем описание для указанного буфера полученного объекта
   obj.SetBufferDescription(buffer_num,descr);
  }
//+------------------------------------------------------------------+
//| Возвращает флаг серийности указанного буфера                     |
//+------------------------------------------------------------------+
bool CMSTFIndicators::IsSeries(const int ind_handle,const uint buffer_num) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return false;
     }
//--- Возвращаем флаг серийности указанного буфера полученного объекта
   return obj.IsSeries(buffer_num);
  }
//+------------------------------------------------------------------+
//| Возвращает флаг синхронизированности                             |
//| исторических данных по символу/периоду                           |
//+------------------------------------------------------------------+
bool CMSTFIndicators::IsSynchronized(const int ind_handle) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return false;
     }
//--- Возвращаем флаг синхронизированности полученного объекта
   return obj.IsSynchronized();
  }
//+------------------------------------------------------------------+
//| Возвращает таймфрейм указанного индикатора                       |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES CMSTFIndicators::Timeframe(const int ind_handle) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return WRONG_VALUE;
     }
//--- Возвращаем таймфрейм полученного объекта
   return obj.Timeframe();
  }
//+------------------------------------------------------------------+
//| Возвращает символ указанного индикатора                          |
//+------------------------------------------------------------------+
string CMSTFIndicators::Symbol(const int ind_handle) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
//--- Если объект получен, возвращаем наименование символа. Иначе - текст ошибки
   return(obj!=NULL ? obj.Symbol() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__));
  }
//+------------------------------------------------------------------+
//| Возвращает наименование указанного индикатора                    |
//+------------------------------------------------------------------+
string CMSTFIndicators::Name(const int ind_handle) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
//--- Если объект получен, возвращаем наименование индикатора. Иначе - текст ошибки
   return(obj!=NULL ? obj.Name() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__));
  }
//+------------------------------------------------------------------+
//| Возвращает список параметров указанного индикатора               |
//+------------------------------------------------------------------+
string CMSTFIndicators::Parameters(const int ind_handle) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
//--- Если объект получен, возвращаем список параметров индикатора. Иначе - текст ошибки
   return(obj!=NULL ? obj.Parameters() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__));
  }
//+------------------------------------------------------------------+
//| Возвращает Digits указанного индикатора                          |
//+------------------------------------------------------------------+
int CMSTFIndicators::Digits(const int ind_handle) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return WRONG_VALUE;
     }
//--- Возвращаем Digits полученного объекта
   return obj.Digits();
  }
//+------------------------------------------------------------------+
//| Возвращает количество буферов указанного индикатора              |
//+------------------------------------------------------------------+
uint CMSTFIndicators::BuffersTotal(const int ind_handle) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return 0;
     }
//--- Возвращаем количество буферов полученного объекта
   return obj.BuffersTotal();
  }
//+------------------------------------------------------------------+
//| Возвращает количество баров таймсерии для указанного индикатора  |
//+------------------------------------------------------------------+
uint CMSTFIndicators::RatesTotal(const int ind_handle) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return 0;
     }
//--- Возвращаем количество баров таймсерии полученного объекта
   return obj.RatesTotal();
  }
//+------------------------------------------------------------------+
//| Возвращает идентификатор указанного индикатора                   |
//+------------------------------------------------------------------+
int CMSTFIndicators::ID(const int ind_handle) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return WRONG_VALUE;
     }
//--- Возвращаем идентификатор полученного объекта
   return obj.ID();
  }
//+------------------------------------------------------------------+
//| Возвращает описание указанного индикатора                        |
//+------------------------------------------------------------------+
string CMSTFIndicators::Description(const int ind_handle) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
//--- Если объект получен, возвращаем описание индикатора. Иначе - текст ошибки
   return(obj!=NULL ? obj.Description() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__));
  }
//+------------------------------------------------------------------+
//| Возвращает заголовок указанного индикатора                       |
//+------------------------------------------------------------------+
string CMSTFIndicators::Title(const int ind_handle) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
//--- Если объект получен, возвращаем заголовок индикатора. Иначе - текст ошибки
   return(obj!=NULL ? obj.Title() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__));
  }
//+------------------------------------------------------------------+
//| Возвращает категорию указанного индикатора                       |
//+------------------------------------------------------------------+
ENUM_IND_CATEGORY CMSTFIndicators::Category(const int ind_handle) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return IND_CATEGORY_NONE;
     }
//--- Возвращаем категорию полученного объекта
   return obj.Category();
  }
//+------------------------------------------------------------------+
//| Возвращает количество параметров указанного индикатора           |
//+------------------------------------------------------------------+
uint CMSTFIndicators::ParamsTotal(const int ind_handle) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return 0;
     }
//--- Возвращаем количество параметров полученного объекта
   return obj.ParamsTotal();
  }
//+------------------------------------------------------------------+
//| Возвращает структуру параметров по индексу из массива            |
//| для указанного индикатора                                        |
//+------------------------------------------------------------------+
MqlParam CMSTFIndicators::GetMqlParam(const int ind_handle,const int index) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   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 obj.GetMqlParam(index);
  }
//+------------------------------------------------------------------+
//| Возвращает описание таймфрейма для указанного индикатора         |
//+------------------------------------------------------------------+
string CMSTFIndicators::TimeframeDescription(const int ind_handle) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
//--- Если объект получен, возвращаем описание таймфрейма индикатора. Иначе - текст ошибки
   return(obj!=NULL ? obj.Description() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__));
  }
//+------------------------------------------------------------------+
//| Возвращает количество рассчитанных данных указанного индикатора  |
//+------------------------------------------------------------------+
int CMSTFIndicators::Calculated(const int ind_handle) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return WRONG_VALUE;
     }
//--- Возвращаем количество рассчитанных данных полученного объекта
   return obj.Calculated();
  }
//+------------------------------------------------------------------+
//| Возвращает тип указанного индикатора                             |
//+------------------------------------------------------------------+
ENUM_INDICATOR CMSTFIndicators::Type(const int ind_handle) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return (ENUM_INDICATOR)WRONG_VALUE;
     }
//--- Возвращаем тип индикатора полученного объекта
   return (ENUM_INDICATOR)obj.Type();
  }

A lógica de todos os métodos é comentada na listagem dos métodos. Primeiro, obtemos um ponteiro para o indicador desejado na lista, em seguida, definimos ou retornamos sua propriedade, ou realizamos um cálculo e retornamos seu resultado. Todos os métodos chamados foram discutidos acima, quando discutimos a classe base do indicador multiperíodo com vários símbolos.

Implementação de métodos para criar novos objetos indicadores:

//+------------------------------------------------------------------+
//| Добавляет указанный индикатор в коллекцию                        |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewIndicator(CIndMSTF *ind_obj,const string source)
  {
//--- Устанавливаем списку коллекции флаг сортированного списка
   this.m_list.Sort();
//--- Ищем в списке индекс аналогичного переданному в метод объекта-индикатора
   int index=this.m_list.Search(ind_obj);
//--- Если такой индикатор с такими же параметрами уже есть в списке,
   if(index>WRONG_VALUE)
     {
      //--- сообщаем об этом в журнал и удаляем новый объект-индикатор
      ::PrintFormat("%s: The %s indicator with such parameters %s is already in the collection",source,ind_obj.Name(),ind_obj.Parameters());
      delete ind_obj;
      //--- Получаем указатель на уже существующий объект-индикатор в списке и возвращаем его хэндл
      ind_obj=this.m_list.At(index);
      return(ind_obj!=NULL ? ind_obj.Handle() : INVALID_HANDLE);
     }
//--- Если такого индикатора нет в списке, но его не удалось поместить в список
   if(!this.m_list.Add(ind_obj))
     {
      //--- сообщаем об ошибке в журнал, удаляем объект-индикатор и возвращаем INVALID_HANDLE
      ::PrintFormat("%s: Error. Failed to add %s indicator to collection",source,ind_obj.Name());
      delete ind_obj;
      return INVALID_HANDLE;
     }
//--- Если индикатор помещён в список, но для него не удалось создать расчётную часть - возвращаем INVALID_HANDLE
//--- (при ошибке создания расчётной части из списка объект-индикатор удаляется в методе CreateIndicator)
   if(!this.CreateIndicator(ind_obj))
      return INVALID_HANDLE;
//--- Всё успешно - сообщаем о добавлении нового индикатора в коллекцию и возвращаем его хэндл
   ::PrintFormat("%s: %s indicator (handle %ld) added to the collection",source,ind_obj.Title(),ind_obj.Handle());
   return ind_obj.Handle();
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Accelerator Oscillator           |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewAC(const string symbol,const ENUM_TIMEFRAMES timeframe)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Accumulation/Distribution        |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewAD(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+--------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Average Directional Movement Index |
//+--------------------------------------------------------------------+
int CMSTFIndicators::AddNewADX(const string symbol,const ENUM_TIMEFRAMES timeframe,const int adx_period=14)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор                                  |
//| Average Directional Movement Index by Welles Wilder              |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewADXWilder(const string symbol,const ENUM_TIMEFRAMES timeframe,const int adx_period=14)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Alligator                        |
//+------------------------------------------------------------------+
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)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Adaptive Moving Average          |
//+------------------------------------------------------------------+
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)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Awesome Oscillator               |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewAO(const string symbol,const ENUM_TIMEFRAMES timeframe)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Average True Range               |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewATR(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Bears Power                      |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewBearsPower(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=13)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Bulls Power                      |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewBullsPower(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=13)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Bollinger Bands®                 |
//+------------------------------------------------------------------+
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)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Commodity Channel Index          |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewCCI(const string symbol,const ENUM_TIMEFRAMES timeframe,
                               const int ma_period=14,
                               const ENUM_APPLIED_PRICE applied_price=PRICE_TYPICAL)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Chaikin Oscillator               |
//+------------------------------------------------------------------+
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)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+-------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Double Exponential Moving Average |
//+-------------------------------------------------------------------+
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)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор DeMarker                         |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewDeMarker(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Envelopes                        |
//+------------------------------------------------------------------+
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)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Force Index                      |
//+------------------------------------------------------------------+
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)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Fractals                         |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewFractals(const string symbol,const ENUM_TIMEFRAMES timeframe)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Fractal Adaptive Moving Average  |
//+------------------------------------------------------------------+
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)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Gator                            |
//+------------------------------------------------------------------+
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)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Ichimoku Kinko Hyo               |
//+------------------------------------------------------------------+
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)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Market Facilitation Index        |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewBWMFI(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Momentum                         |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewMomentum(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                    const int mom_period=14,
                                    const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Money Flow Index                 |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewMFI(const string symbol,const ENUM_TIMEFRAMES timeframe,
                               const int ma_period=14,
                               const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Moving Average                   |
//+------------------------------------------------------------------+
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)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Moving Average of Oscillator     |
//+------------------------------------------------------------------+
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)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор                                  |
//| 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)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор On Balance Volume                |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewOBV(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+-------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Parabolic Stop and Reverse system |
//+-------------------------------------------------------------------+
int CMSTFIndicators::AddNewSAR(const string symbol,const ENUM_TIMEFRAMES timeframe,
                               const double step=0.02,
                               const double maximum=0.2)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Relative Strength Index          |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewRSI(const string symbol,const ENUM_TIMEFRAMES timeframe,
                               const int ma_period=14,
                               const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Relative Vigor Index             |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewRVI(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=10)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Standard Deviation               |
//+------------------------------------------------------------------+
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)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Stochastic Oscillator            |
//+------------------------------------------------------------------+
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)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+-------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Triple Exponential Moving Average |
//+-------------------------------------------------------------------+
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)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
  
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор                                  |
//| 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)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Larry Williams' Percent Range    |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewWPR(const string symbol,const ENUM_TIMEFRAMES timeframe,const int calc_period=14)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Variable Index Dynamic Average   |
//+------------------------------------------------------------------+
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)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Volumes                          |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewVolumes(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }
//+------------------------------------------------------------------+
//| Добавляет в коллекцию пользовательский индикатор                 |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewCustom(const string symbol,const ENUM_TIMEFRAMES timeframe,const string path,const string name,const uint buffers,const MqlParam &param[])
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем 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 this.AddNewIndicator(ind_obj,__FUNCTION__);
  }

Todos os códigos para a classe de coleção de indicadores estão prontos. A lógica de cada método é comentada, e entender por conta própria não será difícil.


Testes

Para testar o trabalho com classes de multi-indicadores e sua coleção, criaremos um indicador simples no qual faremos a escolha entre várias médias móveis. O objetivo dos testes de hoje é testar o trabalho das classes com indicadores que desenham uma única linha de um buffer na janela principal do gráfico. As médias móveis do conjunto de indicadores de tendência do terminal do cliente são bem adequadas para essa finalidade. No artigos a seguir, criaremos modelos para a criação rápida e trabalho com quaisquer indicadores do conjunto de indicadores no terminal + trabalho com indicadores personalizados. E gradualmente aprimoraremos completamente as classes criadas hoje para o trabalho correto com quaisquer tipos de indicadores.

O indicador de teste mostrará as linhas dos multi-indicadores criados no gráfico, e seus dados serão exibidos no painel de informações, descrito no primeiro artigo do ciclo de trabalho com indicadores.

Dentro de um indicador, criaremos dois indicadores idênticos. Um será calculado com base nos dados do gráfico atual e o outro, com base nos dados do símbolo/período gráfico selecionados nas configurações. Assim, sempre poderemos ver a linha da média móvel no gráfico atual e a linha da mesma média móvel, mas construída em outro período gráfico. O símbolo também pode ser escolhido de outro gráfico, mas então as linhas não coincidirão no nível de preço.

Criaremos um novo arquivo de indicador chamado TestMSTFMovingAverages.mq5:




Definiremos os manipuladores OnTimer e OnChartEvent:


Escolheremos dois buffers desenhados na forma de linhas na janela principal do gráfico:


Os nomes dos buffers podem ser quaisquer, já que renomearemos no código. Clicamos em "Pronto" e obtemos o template do indicador:

//+------------------------------------------------------------------+
//|                                       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 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)
  {
//---
   
  }

Na área global adicionaremos uma enumeração para escolher o tipo de média móvel, incluiremos o arquivo da classe de indicadores multissímbolo e multiperíodo e o arquivo com a classe do painel, e declararemos as variáveis de entrada.
Renomearemos os buffers
para tornar seus nomes mais legíveis e declararemos as variáveis globais do indicador:

//+------------------------------------------------------------------+
//|                                       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        */ // Используемый тип скользящей средней
input string               InpSymbol      =  NULL;             /* Symbol         */ // Символ скользящей средней
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe      */ // Таймфрейм скользящей средней
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;      /* Applied Price  */ // Используемая цена для расчёта
input ENUM_MA_METHOD       InpMethod      =  MODE_SMA;         /* MA Method      */ // Метод расчёта Moving Average
input int                  InpShift       =  0;                /* MA Shift       */ // Сдвиг скользящей средней
input bool                 InpAsSeries    =  true;             /* As Series flag */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferMA1[];
double         BufferMA2[];
//--- global variables
int handle_ma1;
int handle_ma2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные

Dentro do manipulador OnInit(), escreveremos a criação de um timer, atribuiremos os buffers desenhados, criaremos handles para os indicadores selecionados e criaremos o painel informativo:

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferMA1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferMA2,INDICATOR_DATA);
//--- sets indicator shift
   //PlotIndexSetInteger(0,PLOT_SHIFT,InpShift);   // аналог в стр. 116
   //PlotIndexSetInteger(1,PLOT_SHIFT,InpShift);   // аналог в стр. 117
//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferMA1,InpAsSeries);
   ArraySetAsSeries(BufferMA2,InpAsSeries);
   
//--- Для разных индикаторов ширина панели (width) будет индивидуальной (из-за количества параметров в описании)
   int width=0;
//--- В зависимости от выбранного в настройках индикатора создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   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(handle_ma1==INVALID_HANDLE || handle_ma2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_ma1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_ma2,0);
//--- Устанавливаем "пустые" значения для буферов расчётной части созданных индикаторов
   indicators.SetBufferInitValue(handle_ma1,0,EMPTY_VALUE);
   indicators.SetBufferInitValue(handle_ma2,0,EMPTY_VALUE);
//--- Устанавливаем смещения линиям индикатора
   indicators.SetPlotShift(0,InpShift);
   indicators.SetPlotShift(1,InpShift);
      
//--- Панель
//--- Создаём панель
   panel=new CDashboard(1,20,20,width,264);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }

Adicionaremos o manipulador OnDeinit():

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }

No manipulador OnCalculate() chamaremos o cálculo de todos os indicadores multissímbolo e multiperíodo. Em caso de cálculo malsucedido, deve-se sair do manipulador retornando zero, para recalcular os indicadores novamente no próximo tick.
Após um cálculo bem-sucedido, os dados nos arrays-buffers dos objetos-indicadores já estão disponíveis, já que eles podem agora ser exibidos no painel informativo. Após exibir os dados no painel, mostraremos os dados dos buffers calculados nos buffers desenhados do indicador:

//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   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 next call
   return(rates_total);
  }

No timer do indicador, chamaremos o timer do objeto-coleção de indicadores:

//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }

No manipulador de eventos do gráfico OnChartEvent(), chamaremos o manipulador de eventos do objeto-painel e processaremos o movimento do cursor para determinar a barra sobre a qual ele está posicionado:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

Função que exibe os dados dos multi-indicadores no painel:

//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   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);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   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);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 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);
   
//--- Выводим описание состояния линии индикатора 1
   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);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 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);
   
//--- Выводим описание состояния линии индикатора 2
   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);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   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);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }

Como vemos, para calcular os indicadores multissímbolo e multiperíodo, basta chamar o método Calculate() da classe-coleção de indicadores. Após um cálculo bem-sucedido, todos os dados já estão disponíveis. Nos Expert Advisors, eles podem ser obtidos e processados. Nos indicadores, após um cálculo bem-sucedido, os dados podem ser exibidos no gráfico na forma de linhas de buffers de desenho, utilizando o método DataToBuffer() da classe de coleção de indicadores. E isso é tudo que é necessário para o cálculo e exibição de multi-indicadores no gráfico.

Após a compilação do indicador de teste, vamos executá-lo em um gráfico com o período M1, nas configurações escolheremos o símbolo atual e o período de cálculo do indicador M5. Neste caso, serão criados dois indicadores de médias móveis selecionados nas configurações. Um será calculado com base nos dados do gráfico atual, e o outro com base nos dados do período gráfico de cinco minutos. Alternando o timeframe do gráfico, será possível ver como no M1 são desenhadas duas linhas: uma corresponderá à média móvel calculada no M1, e a outra à média móvel calculada no M5. Se mudarmos o gráfico para M5, apenas um indicador será criado, pois o segundo será idêntico ao primeiro e não será criado. Se mudarmos o gráfico para M15, um indicador será calculado no M15, e o outro no M5, e também será exibido no gráfico.


Como podemos ver, a funcionalidade anunciada funciona ao exibir no janela principal do gráfico indicadores com um buffer.

Todos os arquivos de todas as classes e do indicador de teste podem ser vistos nos arquivos anexados ao artigo.


Considerações finais

Hoje, fizemos funcionalidades para a criação rápida de indicadores multissímbolo e multiperíodo e a obtenção de seus dados em indicadores com plotagem de indicadores calculados na janela principal do gráfico. Nos próximos artigos, criaremos modelos para a criação de outros indicadores padrão multissímbolo e multiperíodo, que plotam seus dados na janela principal do gráfico, e não apenas com um buffer de desenho. No final, teremos uma ferramenta prática para transformar rapidamente quaisquer indicadores em suas versões multissímbolo e multiperíodo. E, provavelmente, as classes de multi-indicadores serão posteriormente aprimoradas com a criação de outros indicadores padrão e personalizados.


Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/13578

Arquivos anexados |
Dashboard.mqh (217.85 KB)
IndMSTF.mqh (473.51 KB)
Permutação das barras de preços no MQL5 Permutação das barras de preços no MQL5
Neste artigo, apresentamos um algoritmo de permutação das barras de preços e detalhamos como os testes de permutação podem ser usados para identificar casos em que o desempenho de uma estratégia é inventado com o objetivo de enganar potenciais compradores de Expert Advisors.
Experimentos com redes neurais (Parte 7): Transferência de indicadores Experimentos com redes neurais (Parte 7): Transferência de indicadores
Desta vez, veremos exemplos de passagem de indicadores ao perceptron. Abordaremos conceitos gerais, um Expert Advisor simples pronto, os resultados de sua otimização e testes forward.
Adicionando um LLM personalizado a um robô investidor (Parte 2): Exemplo de implementação de ambiente Adicionando um LLM personalizado a um robô investidor (Parte 2): Exemplo de implementação de ambiente
Os modelos de linguagem (LLMs) são uma parte importante da inteligência artificial que evolui rapidamente. E para aproveitar isso devemos pensar em como integrar LLMs avançados em nossa negociação algorítmica Muitos acham desafiador ajustar esses modelos de acordo com suas necessidades, implantá-los localmente e, logo, aplicá-los à negociação algorítmica. Esta série de artigos explorará uma abordagem passo a passo para alcançar esse objetivo.
Redes neurais de maneira fácil (Parte 60): transformador de decisões on-line (ODT) Redes neurais de maneira fácil (Parte 60): transformador de decisões on-line (ODT)
As últimas 2 partes foram dedicadas ao método transformador de decisões (DT), que modela sequências de ações no contexto de um modelo autorregressivo de recompensas desejadas. Neste artigo, vamos considerar outro algoritmo de otimização deste método.