оптимизация iCustom

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

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

Хотя наиболее оптимальный вариант - повторить расчет данных индикатора в советнике, адаптировав этот расчет именно для нужд советника. В подавляющем большинстве случаев код индикатора для советника выходит намного лаконичнее и работает быстрее, чем индикатор.

 
Ihor Herasko:

В самом простом случае нужно именно так и сделать: вызвать iCustom нужное количество раз. Ведь это не ведет к реальным расчетам данных всего индикатора. 

Вы уверены, что пересчета не будет? Речь идет о mql4
Посмотрел в mql5 iCustom сделан по уму, позволяющий не вызывать несколько раз iCustom.

 
peterlogin:

Вы уверены, что пересчета не будет? Речь идет о mql4
Посмотрел в mql5 iCustom сделан по уму, позволяющий не вызывать несколько раз iCustom.

это легко проверить с помощью профилирования, если входные параметры не менялись то обращение к другим буферам индикатора не требует перерасчёта
 
peterlogin:

Вы уверены, что пересчета не будет? Речь идет о mql4
Посмотрел в mql5 iCustom сделан по уму, позволяющий не вызывать несколько раз iCustom.

Если индикатор написан правильно (с контролем баров, на которых он вызван), то пересчета не будет.

 

интересная тема, вот для проверки индикатор (tst_icustom.mql4):

#property copyright "Copyright 2019, IgorM"
#property link      "https://www.mql5.com/ru/users/igorm"
#property version   "1.00"
#property strict
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots   3
//--- plot Label1
#property indicator_label1  "Label1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot Label2
#property indicator_label2  "Label2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrOrange
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- plot Label3
#property indicator_label3  "Label3"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrBlue
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1
//--- indicator buffers
input double   value=1.0;
double         Label1Buffer[];
double         Label2Buffer[];
double         Label3Buffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,Label1Buffer);
   SetIndexBuffer(1,Label2Buffer);
   SetIndexBuffer(2,Label3Buffer);

//---
   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[])
  {
   int i,limit;
   if(prev_calculated==0) limit=rates_total-1; else limit=rates_total-prev_calculated+1;
   Print("limit = ",limit," , value = ",value);
   for(i=limit; i>=0; i--)
     {
      Label1Buffer[i]=close[i] * value;
      Label2Buffer[i]=close[i] * value;
      Label3Buffer[i]=close[i] * value;
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+

и советник который вызовет индикатор на каждом тике:

#property copyright "Copyright 2019, IgorM"
#property link      "https://www.mql5.com/ru/users/igorm"
#property version   "1.00"
#property strict
//--- input parameters
input double   Value1 = 1.0;
input double   Value2 = 2.0;
input double   Value3 = 3.0;

void OnTick()
  {
   double ind_buf1=iCustom(NULL,0,"tst_icustom",Value1,0,1);
   Print("Вызов буффера 1 , результат = ",ind_buf1);
   double ind_buf2=iCustom(NULL,0,"tst_icustom",Value2,1,1);
   Print("Вызов буффера 2 , результат = ",ind_buf2);
   double ind_buf3=iCustom(NULL,0,"tst_icustom",Value3,2,1);
   Print("Вызов буффера 3 , результат = ",ind_buf3);
   Comment("ind_buf1 = ",ind_buf1,"\n","ind_buf2 = ",ind_buf2,"\n","ind_buf3 = ",ind_buf3);
  }

в таком виде в логе будет 3 принта от индикатора с при первом вызове 

2019.05.07 17:12:12.131 tst_ic EURUSD,H1: Вызов буффера 3 , результат = 3.354810000000001

2019.05.07 17:12:12.115 tst_icustom EURUSD,H1: limit = 133047 , value = 3.0

2019.05.07 17:12:12.100 tst_ic EURUSD,H1: Вызов буффера 2 , результат = 2.23654

2019.05.07 17:12:12.084 tst_icustom EURUSD,H1: limit = 133047 , value = 2.0

2019.05.07 17:12:12.068 tst_ic EURUSD,H1: Вызов буффера 1 , результат = 1.11827

2019.05.07 17:12:12.068 tst_icustom EURUSD,H1: limit = 133047 , value = 1.0

а если код эксперта написать так:

void OnTick()
  {
   double ind_buf1=iCustom(NULL,0,"tst_icustom",Value1,0,1);
   Print("Вызов буффера 1 , результат = ",ind_buf1);
   double ind_buf2=iCustom(NULL,0,"tst_icustom",Value1,1,1);
   Print("Вызов буффера 2 , результат = ",ind_buf2);
   double ind_buf3=iCustom(NULL,0,"tst_icustom",Value1,2,1);
   Print("Вызов буффера 3 , результат = ",ind_buf3);
   Comment("ind_buf1 = ",ind_buf1,"\n","ind_buf2 = ",ind_buf2,"\n","ind_buf3 = ",ind_buf3);
  }

то принты будут так:

2019.05.07 17:14:01.054 tst_icustom EURUSD,H1: limit = 1 , value = 1.0

2019.05.07 17:14:00.710 tst_ic EURUSD,H1: Вызов буффера 3 , результат = 1.11827

2019.05.07 17:14:00.710 tst_ic EURUSD,H1: Вызов буффера 2 , результат = 1.11827

2019.05.07 17:14:00.710 tst_ic EURUSD,H1: Вызов буффера 1 , результат = 1.11827

2019.05.07 17:14:00.710 tst_icustom EURUSD,H1: limit = 133047 , value = 1.0


итого, примерно так:

- если вызов индикатора с несколькими буферами из эксперта происходит с одинаковыми параметрами, тогда индикатор будет рассчитан один раз и при последующем вызове iCustom() на этом же тике будут просто возвращены данные буферов

- если вызов индикатора ... с разными параметрами, то вроде как будут или рассчитаны все буфера при каждом вызове или создается несколько копий этого индикатора? - тут пока не знаю как проверить копии индикаторов 


ЗЫ: но однозначно оптимизация кода MQL  на высоте, все что можно было оптимизировать - компилятор сделал!

 
Igor Makanu:

- если вызов индикатора ... с разными параметрами, то вроде как будут или рассчитаны все буфера при каждом вызове или создается несколько копий этого индикатора?

Несколько.

 
Andrey Khatimlianskii:

Несколько.

несколько - тогда это производительнее все будет работать, хотя все в одном интерфейсном потоке эксперта... но однозначно разработчики оптимизацию делали на совесть!

как проверить, что много копий индикатора создано?

 
peterlogin:
Поделитесь опытом, кто как выходил из ситуации.
Задача в следующем: есть индикатор в нем рассчитываются несколько параметров, как их вернуть в советник не вызывая несколько раз iCustom для каждого параметра (буфера)?

когда-то что-то такое делал, потому как индикатор должен был возвращать много-много флагов, а заводить по отдельному буферу на флаг как-то жаба давила :-)

битовые флаги упихиваются в битовые поля ulong x = (FLAG0 | (FLAG1<<1) | (FLAG2<<2) и копируются в doublе, на принимающей стороне обратный разворот. x=(ulong)value; FLAG0=(x & 1); FLAG1=((x>>1)&1)); etc

по идее через каждый буфер можно таскать даже структуры, но пока они помещаются в sizeof(double) и желательно не совпадают с EMPTY_VALUE.. 

а через группу буферов N*sizeof(double) можно проносить весьма объёмные вещи,

 
Igor Makanu:

несколько - тогда это производительнее все будет работать, хотя все в одном интерфейсном потоке эксперта... но однозначно разработчики оптимизацию делали на совесть!

как проверить, что много копий индикатора создано?

сравнить объем занимаемой памяти при вызове одног индикатора и при вызове 40 индикаторов (параметры обязательно должны быть разные)

 
Igor Makanu:

несколько - тогда это производительнее все будет работать, хотя все в одном интерфейсном потоке эксперта...

В зависимости от задачи.

И индикаторы не в потоке эксперта, а в потоке таймсерии (символ/тф).


Igor Makanu:

как проверить, что много копий индикатора создано?

В логе есть запись о запуске каждой копии. Этого не достаточно?