Discussing the article: "Creating multi-symbol, multi-period indicators"

 

Check out the new article: Creating multi-symbol, multi-period indicators.

In this article, we will look at the principles of creating multi-symbol, multi-period indicators. We will also see how to access the data of such indicators from Expert Advisors and other indicators. We will consider the main features of using multi-indicators in Expert Advisors and indicators and will see how to plot them through custom indicator buffers.

After compiling the test indicator, we will launch it on a chart with a period of M1, in the settings we will select the current symbol and the calculation period of the indicator M5. In this case, two moving averages selected in the indicator settings will be created. One of them will be calculated using the current chart data, and the second one will be based on the data from a five-minute chart period. By switching the chart timeframe, you can see how two lines are drawn on M1: one will correspond to the moving average calculated on M1, and the second line will correspond to the moving average calculated on M5. If you switch the chart to M5, only one indicator will be created, since the second one will be identical to the first one, and it will not be created. If you switch the chart to M15, then one indicator will be calculated for M15, and the second for M5, and it will also be displayed on the chart.


As you can see, the declared functionality works and we can see indicators in the main chart window. Multiple indicators are created while using one buffer.

Author: Artyom Trishkin

 

Thank you to the author. There is something to think about...

Imho, if the class is basic, then its name should be CBaseIndMSTF. Or even CBaseIndMSMTF. MSMTF - multi-symbol multi-timeframe.

By name.

ENUM_IND_CATEGORY is good.

The following are not so good.

ENUM_COMPARE_MODE --> ENUM_IND_COMPARE_MODE

ENUM_LINE_STATE --> ENUM_IND_LINE_STATE

ENUM_ERR_TYPE --> ENUM_IND_CALC_ERR_TYPE


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

ERR_TYPE_NO_CYNC --> ERR_TYPE_NO_SYNC

 
Denis Kirichenko multi-symbol multi-timeframe.

By names.

ENUM_IND_CATEGORY is fine.

The following are not good.

ENUM_COMPARE_MODE --> ENUM_IND_COMPARE_MODE

ENUM_LINE_STATE --> ENUM_IND_LINE_STATE

ENUM_ERR_TYPE --> ENUM_IND_CALC_ERR_TYPE


ERR_TYPE_NO_CYNC --> ERR_TYPE_NO_SYNC

I agree withthis one. The others are specific

 
to sync/to synchronize
 
Denis Kirichenko #:
to sync/to synchronize

I can't make sense of it. Two s's are highlighted, so what?

"No Sync" - not synchronised. What's wrong? "Error: No Synchronized"

Aaah!

I saw C instead of S at the last minute... Okay. Thanks. I'll fix it.

 

Artem, ran the TestMSTFMovingAverages indicator on the EURUSD chart. Weekend day. Tf1 = M15, Tf2 = H1.

Input parameters:

InpIndicator=26
InpSymbol=
InpTimeframe=16385
InpPrice=1
InpMethod=0
InpShift=0
InpAsSeries=true

This is what came out:

Logs:

HD      0       20:22:12.149    TestMSTFMovingAverages (EURUSD,M15)     CMSTFIndicators::AddNewMA: MA(10) indicator (handle 10) added to the collection
PJ      0       20:22:12.150    TestMSTFMovingAverages (EURUSD,M15)     CMSTFIndicators::AddNewMA: MA(EURUSD,H1:10) indicator (handle 11) added to the collection
CG      0       20:22:12.179    TestMSTFMovingAverages (EURUSD,M15)     CIndMSTF::Calculate::MA(EURUSD,H1:10): Start downloading data by EURUSD/H1. Waiting for the next tick...
OG      0       20:22:12.179    TestMSTFMovingAverages (EURUSD,M15)     CMSTFIndicators::Calculate::MA(EURUSD,H1:10): Error in indicator calculation: Data not loaded
HQ      0       20:22:12.179    TestMSTFMovingAverages (EURUSD,M15)     CMSTFIndicators::Calculate: Not all indicators have been calculated successfully. It is necessary to recalculate the buffers of all indicators
QH      0       20:22:12.224    TestMSTFMovingAverages (EURUSD,M15)     CIndMSTF::DataToBuffer::MA(EURUSD,H1:10) First start, or historical data has been changed. Initialize Buffer(0)


Highlighted the candle at 23:00 with a crosshair. You can see that the red SMA10 line changes its value only on the next candle after 23:00. I.e. we have a transition of values at 23: 00 and 23:15.

In my understanding, the transition from the current to the next value on Tf2 should take place at22: 45 and 23:00.


Then this is what I got. I run the SMA10 indicator with offset = 1 on a separate EURUSD-H1 chart.

I open a new chart and run TestMSTFMovingAverages. And miracle, I see that the picture seems to be corrected.


Logs:

2023.10.29 20:38:42.583 TestMSTFMovingAverages (EURUSD,M15)     CMSTFIndicators::AddNewMA: MA(10) indicator (handle 10) added to the collection
2023.10.29 20:38:42.584 TestMSTFMovingAverages (EURUSD,M15)     CMSTFIndicators::AddNewMA: MA(EURUSD,H1:10) indicator (handle 11) added to the collection
2023.10.29 20:38:42.622 TestMSTFMovingAverages (EURUSD,M15)     CIndMSTF::DataToBuffer::MA(10) First start, or historical data has been changed. Initialize Buffer(0)
2023.10.29 20:38:42.654 TestMSTFMovingAverages (EURUSD,M15)     CIndMSTF::DataToBuffer::MA(EURUSD,H1:10) First start, or historical data has been changed. Initialize Buffer(0)


And both in the first case and in the second case on the panel at 23:00 the value of MA(EURUSD,H1:10) = 1.056959. Which, imho, is correct. Apparently, in the first case something is wrong with the buffer filling for MA(EURUSD,H1:10).

 
Denis Kirichenko EURUSD-H1 chart.

I open a new chart and run TestMSTFMovingAverages. And miracle, I see that the picture seems to be corrected.


Logs:


Moreover, both in the first case and in the second case on the panel at 23:00 the value of MA(EURUSD,H1:10) = 1.056959. Which, imho, is correct. Apparently, in the first case something is wrong with the buffer filling for MA(EURUSD,H1:10).

Thanks, Denis. I will take a look later - I am busy continuing this topic and I don't have time yet, unfortunately.
 

...При работе с данными не текущего графика для исключения "освобождения" таймсерии, необходимо не реже. чем раз в две минуты обращаться к этой таймсерии. В этом случае будет происходить "удержание" таймсерии, что ускорит к ней обращение (не нужно будет каждый раз дожидаться синхронизации данных)...

How did you get that frequency value? Was it determined experimentally?

 
Denis Kirichenko #:

How did you get that frequency value? Was it determined experimentally?

Message from the developers on the forum.

 

There is such a structure:

//--- 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);     }
  };

Since there are set\get-methods, then imho theprivate access specifier is missing:

//--- struct
struct SBuffer                // Структура индикаторного буфера
   {
   private:
      double            array[];    // Массив-буфер индикатора
      double            init_value; // Инициализирующее значение
      int               shift;      // Сдвиг буфера по горизонтали
      string            descript;   // Описание буфера
   public:
      //--- (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);
         }
   };


And we also need methods for working with the buffer...

 
Denis Kirichenko #:

There is such a structure:

Since there are set\get-methods, then imho theprivate access specifier is missing:


And we also need methods for working with the buffer...

Initially I tried not to complicate things.

Reason: