Базовые индикаторы, применяемые к кастомному инструменту

 

Здравствуйте. Задавал этот вопрос на английском форуме. Ничего, кроме совета вставить исходные коды требуемых базовых индикаторов в код своего индикатора MQL5, не получил.

Итак, существует некий кастомный временной ряд, который рассчитывается на каждом баре. К нему требуется применить (без GUI) базовые индикаторы (допустим, MA, BB, Env, не важно). Если бы это был дефолтный инструмент, мы бы просто воспользовались техническим индикатором, к примеру, iMA, в котором бы указали его наименование, получили бы хендл и не знали бы проблем. Но в случае, если мы инструмент создаётся синтетическим путём, указать его напрямую мы не можем. Для этого приходится в индикаторе iMA указывать хендл еще одного отдельного индикатора, единственная задача которого - считать кастомный временной ряд, чтобы в дальнейшем передать его в наш первоначальный индикатор в качестве хендла. Таким образом, получается своеобразный костыль, в котором существуют два индикатора вместо одного (на примере MQL4 и технических индикаторов iMaOnArray, iRsiOnArray и т.п.).

Итак, вопрос. Каким образом можно осуществить создание расчёт базовых индикаторов на синтетическом временном ряду, реализовав всё в одном индикаторе и не прибегая к копированию полного кода необходимых базовых индикаторов? Спасибо!

 

Запуск индикатора на кастомном символе.

#include <Symbol.mqh> // https://www.mql5.com/ru/code/18855

void OnStart()
{
  const SYMBOL Symb(_Symbol + "_custom"); // Создали символ

  Symb = _Symbol; // Скопировали с основного символа все свойства и баровую историю - клон
  
  const int handle = iMA(Symb.Name, PERIOD_CURRENT, 1, 0, MODE_SMA, PRICE_CLOSE); // индикатор на кастомных данных
  
  double Buffer[];  
  const int Amount = CopyBuffer(handle, 0, 0, 1, Buffer);
  
  Print(Buffer[Amount - 1]);
}
 

В МТ5 у стандартных индикаторов можно указать цену или хэндл другого индикатора. Применится к нулевому буферу. 

 
Roman Zhitnik:

Для этого приходится в индикаторе iMA указывать хендл еще одного отдельного индикатора, единственная задача которого - считать кастомный временной ряд, чтобы в дальнейшем передать его в наш первоначальный индикатор в качестве хендла. Таким образом, получается своеобразный костыль, в котором существуют два индикатора вместо одного (на примере MQL4 и технических индикаторов iMaOnArray, iRsiOnArray и т.п.).

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

 
Roman Zhitnik:

Здравствуйте. Задавал этот вопрос на английском форуме. Ничего, кроме совета вставить исходные коды требуемых базовых индикаторов в код своего индикатора MQL5, не получил.

Итак, существует некий кастомный временной ряд, который рассчитывается на каждом баре. К нему требуется применить (без GUI) базовые индикаторы (допустим, MA, BB, Env, не важно). Если бы это был дефолтный инструмент, мы бы просто воспользовались техническим индикатором, к примеру, iMA, в котором бы указали его наименование, получили бы хендл и не знали бы проблем. Но в случае, если мы инструмент создаётся синтетическим путём, указать его напрямую мы не можем. Для этого приходится в индикаторе iMA указывать хендл еще одного отдельного индикатора, единственная задача которого - считать кастомный временной ряд, чтобы в дальнейшем передать его в наш первоначальный индикатор в качестве хендла. Таким образом, получается своеобразный костыль, в котором существуют два индикатора вместо одного (на примере MQL4 и технических индикаторов iMaOnArray, iRsiOnArray и т.п.).

Итак, вопрос. Каким образом можно осуществить создание расчёт базовых индикаторов на синтетическом временном ряду, реализовав всё в одном индикаторе и не прибегая к копированию полного кода необходимых базовых индикаторов? Спасибо!

Может эти классы подойдут?


 
Roman Zhitnik:

Каким образом можно осуществить создание расчёт базовых индикаторов на синтетическом временном ряду, реализовав всё в одном индикаторе и не прибегая к копированию полного кода необходимых базовых индикаторов? Спасибо!

Если это нужно сделать внутри своего индикатора, то проще всего сделать входной параметр bool CustomData, который сигналирует, вычислять ли кастомную дату (в нулевой буфер) или работать в нормальном режиме. Соответственно, в нормальном режиме вызывать iCustom самого себя с CusomData = true и полученный хэндл запихивать, куда хочется.

// МАшка на кастомных данных
#property indicator_separate_window 
#property indicator_buffers 1 
#property indicator_plots   1 

#property indicator_type1   DRAW_LINE 
#property indicator_color1  clrRed 
#property indicator_style1  STYLE_SOLID 
#property indicator_width1  1 

input bool CustomData = false; // true - кастомный режим для iCustom
input int MAPeriod = 1e3;      // Период МАшки

string GetMyName( void )
{
  const int Length = StringLen(TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Indicators\\");
  const string Path = MQLInfoString(MQL_PROGRAM_PATH);
  
  return(StringSubstr(Path, Length, StringLen(Path) - Length - 4));
}

double Buffer[];
const bool Init = SetIndexBuffer(0, Buffer, INDICATOR_DATA);

const int handleMA = CustomData ? INVALID_HANDLE
                                : iMA(NULL, PERIOD_CURRENT, MAPeriod, 0, MODE_SMA, iCustom(_Symbol, PERIOD_CURRENT, GetMyName(), true));

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 
{
  if (CustomData)
    for (int i = prev_calculated; i < rates_total; i++)
      Buffer[i] = MathRand(); // Подготовили данные
  
  return(CustomData ? rates_total : prev_calculated + CopyBuffer(handleMA, 0, prev_calculated, rates_total - prev_calculated, Buffer));
}
 
fxsaber:

Если это нужно сделать внутри своего индикатора, то проще всего сделать входной параметр bool CustomData, который сигналирует, вычислять ли кастомную дату (в нулевой буфер) или работать в нормальном режиме. Соответственно, в нормальном режиме вызывать iCustom самого себя с CusomData = true и полученный хэндл запихивать, куда хочется.

Да, начал пробовать этот вариант, когда Вы еще без кода сообщение написали. Не знал, что такой вариант рекурсии возможен. То, что надо! Премного благодарен.

Всем спасибо за идеи.

 
fxsaber:

Если это нужно сделать внутри своего индикатора, то проще всего сделать входной параметр bool CustomData, который сигналирует, вычислять ли кастомную дату (в нулевой буфер) или работать в нормальном режиме. Соответственно, в нормальном режиме вызывать iCustom самого себя с CusomData = true и полученный хэндл запихивать, куда хочется.

Таким способом можно достать только нулевой буфер? Или я что-то не так делал?

 
Alexey Viktorov:

Таким способом можно достать только нулевой буфер?

Любой.

 
fxsaber:

Любой.

Сделай пожалуйста пример, что-то у меня CopyBuffer первого буфера даёт ошибку 4806 "Запрошенные данные не найдены".

ps; А из советника достаёт оба буфера DRAW_FILLING.
 
Alexey Viktorov:

Сделай пожалуйста пример, что-то у меня CopyBuffer первого буфера даёт ошибку 4806 "Запрошенные данные не найдены".

ps; А из советника достаёт оба буфера DRAW_FILLING.

Возможно, мы неправильно поняли друг друга. Это имел в виду

// МАшка на двух кастомных данных
#property indicator_separate_window 
#property indicator_buffers 2
#property indicator_plots   2 

#property indicator_type1   DRAW_LINE 
#property indicator_color1  clrRed 
#property indicator_style1  STYLE_SOLID 
#property indicator_width1  1 

#property indicator_type2   DRAW_LINE 
#property indicator_color2  clrYellow
#property indicator_style2  STYLE_SOLID 
#property indicator_width2  1 

input int CustomData = WRONG_VALUE; // Для кастомного режима iCustom
input int MAPeriod = 1e2;           // Период МАшки

string GetMyName( void )
{
  const int Length = StringLen(TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Indicators\\");
  const string Path = MQLInfoString(MQL_PROGRAM_PATH);
  
  return(StringSubstr(Path, Length, StringLen(Path) - Length - 4));
}

const bool FlagCustomData = (CustomData != WRONG_VALUE);

double Buffer0[], Buffer1[];
const bool Init = SetIndexBuffer(0, Buffer0, INDICATOR_DATA) && (!FlagCustomData) && SetIndexBuffer(1, Buffer1, INDICATOR_DATA);

const int handleMA0 = FlagCustomData ? INVALID_HANDLE
                                     : iMA(NULL, PERIOD_CURRENT, MAPeriod, 0, MODE_SMA, iCustom(_Symbol, PERIOD_CURRENT, GetMyName(), 0));
const int handleMA1 = FlagCustomData ? INVALID_HANDLE
                                     : iMA(NULL, PERIOD_CURRENT, MAPeriod, 0, MODE_SMA, iCustom(_Symbol, PERIOD_CURRENT, GetMyName(), 1));

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 
{
  if (FlagCustomData)
    for (int i = prev_calculated; i < rates_total; i++)
      Buffer0[i] = MathRand() * (CustomData + 1); // Подготовили данные
  
  return(FlagCustomData ? rates_total : prev_calculated + MathMin(CopyBuffer(handleMA0, 0, prev_calculated, rates_total - prev_calculated, Buffer0),
                                                                  CopyBuffer(handleMA1, 0, prev_calculated, rates_total - prev_calculated, Buffer1)));
}

Стандартные индикаторы, конечно, считают только нулевые буферы в режиме хэндла. Поэтому для каждого буфера нужно делать свой хэндл.


Причина обращения: