Особенности языка mql5, тонкости и приёмы работы - страница 31

 
Artyom Trishkin:

Ты вообще код глядел, который показываю? А запускал его?

Я спрашивал не о том как заполнить буфер индикатора, а о том, почему если брать значения от АО не с текущего бара, то возвращаются пустые значения.
Это понял - нет истории - она подгружается, и пока она подгружается АО с неродного тф возвращает ошибку "нет данных".

Вопрос теперь стоит так: как узнать что история по нужному тф полностью загружена чтобы не заходить в цикл индикатора?

Если в индикаторе использовать CopyBuffer для получения данных с другого индикатора, то нужно:

  1. Перевернуть свой индикаторный буфер так, чтобы самый правый бар на графике соответствовал индексу "0" в индикаторном буфере.
  2. Таким образом "текущий бар" в индикаторе "iMTF_AO.mq5" - это будет самый ПРАВЫЙ БАР на графике и он будет соответствовать индексу "0" в индикаторном буфере "Buffer[]"
  3. И чтобы получить "текущий бар" с индикатора (CopyBuffer(handle,0,shift,1,array)) параметр "shift" должен быть равен "0".

Не хотите переворачивать индикаторный буфер - сделайте так (временно): AO(0) - это для понимания, как получить ИЗ ИНДИКАТОРА ЗНАЧЕНИЯ ДРУГОГО ИНДИКАТОРА. Возможно один или два раза выскочит ошибка - но это пока не построится таймсерия, а потом значения будут отдаваться стабильно.

 
Artyom Trishkin:
Мой ответ видели? Попробовали?
 
Vladimir Karputov:

Если в индикаторе использовать CopyBuffer для получения данных с другого индикатора, то нужно:

  1. Перевернуть свой индикаторный буфер так, чтобы самый правый бар на графике соответствовал индексу "0" в индикаторном буфере.
  2. Таким образом "текущий бар" в индикаторе "iMTF_AO.mq5" - это будет самый ПРАВЫЙ БАР на графике и он будет соответствовать индексу "0" в индикаторном буфере "Buffer[]"
  3. И чтобы получить "текущий бар" с индикатора (CopyBuffer(handle,0,shift,1,array)) параметр "shift" должен быть равен "0".

Не хотите переворачивать индикаторный буфер - сделайте так (временно): AO(0) - это для понимания, как получить ИЗ ИНДИКАТОРА ЗНАЧЕНИЯ ДРУГОГО ИНДИКАТОРА. Возможно один или два раза выскочит ошибка - но это пока не построится таймсерия, а потом значения будут отдаваться стабильно.

1. В моём коде это есть, и было оно сразу:

   ArraySetAsSeries(Buffer,true);
   int bars=Bars(NULL,PeriodForWork);
   datetime time_limit=GetTime(Symbol(),PeriodForWork,bars-1);
   int limit_p=GetBarShift(Symbol(),Period(),time_limit);
   if(rates_total<1) return(0);

2. Так оно и есть.

3. shift равен индексу цикла i. А цикл идёт от начала исторических данных (rates_total-1) к концу (к текущим данным)

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

 
Artyom Trishkin:

1. В моём коде это есть, и было оно сразу:

2. Так оно и есть.

3. shift равен индексу цикла i. А цикл идёт от начала исторических данных (rates_total-1) к концу (к текущим данным)

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


Переделайте цикл обращения к AO: поставьте от "0" и до какого-то значения (сейчас от какого-то значения до нуля). Когда пойдут ошибки - сразу сравните: кол-во рассчитанных баров ("limit_p") и rates_total текущего индикатора.


Добавлено: и вот эти строки стирают все старания рассчитанного "limit" (если обращаемся к неродному таймфрейму):

   if(limit>1) 
     {
      limit=rates_total-1;
     }

грубо говоря сначала (при обращении к неродному таймфрейму) "limit" стал равен 156, а ниже БАЦ! и "limit" уже стал "154566666666"



 
Alexey Kozitsyn:
Пробовали синхронизацию? Также разработчики советуют поддерживать актуальность данных нужных ТФ/символа через таймер.

Нет, не пробовал ещё. Про поддержку актуальности данных видел, помню-знаю.

Но изначально нужно выходить из OnCalculate() если данные по заданному тф и текущему ещё не синхронизированы.

Про текущий понятно:

if(rates_total<1) return(0);

Про заданный - нужно проверять его Bars() - их количество вместо rates_total.

А вот с limit ещё не сообразил - его на текущем тф можно проверить так (как пример из не этого индикатора):

   int limit=rates_total-prev_calculated;
   if(limit>1) {
      limit=rates_total-4;
      ArrayInitialize(BufferAoDN,EMPTY_VALUE);
      ArrayInitialize(BufferAoUP,EMPTY_VALUE);
      ArrayInitialize(BufferMacdDN,EMPTY_VALUE);
      ArrayInitialize(BufferMacdUP,EMPTY_VALUE);
      ArrayInitialize(BufferRsiDN,EMPTY_VALUE);
      ArrayInitialize(BufferRsiUP,EMPTY_VALUE);
      ArrayInitialize(BufferStochDN,EMPTY_VALUE);
      ArrayInitialize(BufferStochUP,EMPTY_VALUE);
      }

Но в этом тестовом индикаторе данные получаем как с текущего тф (на нём идёт отображение расчётов в OnCalculate()), так и с заданного - для получения данных АО с заданного тф.

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

Может быть, я по своему обыкновению стараюсь сразу всё продумать, и думаю много лишнего...

 
Vladimir Karputov:


Переделайте цикл обращения к AO: поставьте от "0" и до какого-то значения (сейчас от какого-то значения до нуля). Когда пойдут ошибки - сразу сравните: кол-во рассчитанных баров ("limit_p") и rates_total текущего индикатора.


Добавлено: и вот эти строки стирают все старания рассчитанного "limit" (если обращаемся к неродному таймфрейму):

грубо говоря сначала (при обращении к неродному таймфрейму) "limit" стал равен 156, а ниже БАЦ! и "limit" уже стал "154566666666"

Да, про это я сразу написал что напортачил тут второпях.

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

 
Artyom Trishkin:

Нет, не пробовал ещё. Про поддержку актуальности данных видел, помню-знаю.

Но изначально нужно выходить из OnCalculate() если данные по заданному тф и текущему ещё не синхронизированы.

Про текущий понятно:

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

Про выход из OnCalculate(): первый запрос Bars() делаете на этапе инициализации, дальше в OnCalculate() проверяете синхронизации текущего и нужного ТФ-ов. Если нет синхронизации - выход.

 
Artyom Trishkin:

Да, про это я сразу написал что напортачил тут второпях.

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

Артём, что значит "от начала к концу"?

А думаешь переворачивать все массивы лучший выход?

Зачем

ArraySetAsSeries(Buffer,true);

если копируешь всего 1 значение?

В функцию CopyBuffer() при копировании не текущего ТФ лучше передать время нужного бара. Иначе будет копироваться не тот бар который хочешь.

 
Alexey Viktorov:

1. Артём, что значит "от начала к концу"?

2. А думаешь переворачивать все массивы лучший выход?

3. Зачем

4. если копируешь всего 1 значение?

В функцию CopyBuffer() при копировании не текущего ТФ лучше передать время нужного бара. Иначе будет копироваться не тот бар который хочешь.

1. Начало исторических данных - самый первый бар в истории, имеющий наименьшее время открытия, конец исторических данных - текущий бар.

2. С четвёрки на пятёрку переписываю.

3. Ну потому, что в Buffer[] записываются данные в цикле от rates_total-1 до 0. Если не делать его как таймсерию, то отображение будет на графике задом-наперёд.

4. Копирую в один момент времени одно значение, соответствующее данным бара i заданного тф.

ЗЫ. Смотри как лаконично и легко получать данные не текущего тф в mql4, и они получаются успешно, и всё работает как задумано. И как странно получать данные индикатора в mql5, и они не получаются никак - всегда ошибка 4806 если в функцию получения данных АО передать таймфрейм, не соответствующий таймфрейму графика, на котором работает индикатор.

//+------------------------------------------------------------------+
double GetDataAO(string sy, int timeframe, int shift) {
   double array[1];
   ZeroMemory(array);
#ifdef __MQL4__
   array[0]=iAO(sy,timeframe,shift);
#else 
   ResetLastError();
   if(CopyBuffer(handle_ao,0,shift,1,array)==WRONG_VALUE) {
      Print(__FUNCTION__," > Error: ",GetLastError());
      return(0);
      }
#endif 
   return(array[0]);
}
//+------------------------------------------------------------------+

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

handle_ao=iAO(symbol,periodForWork);

Т.е., всегда, в любой момент времени, как бы мы не переключали тф, хэндл создаётся с нужным (выбранным в настройках) тф. Но данные АО можно получить лишь при совпадении выбранного тф в настройках и текущего тф. Если они не совпадают, то всегда возвращается ноль из функции.

Вопрос: ПОЧЕМУ?

 
Artyom Trishkin:

1. Начало исторических данных - самый первый бар в истории, имеющий наименьшее время открытия, конец исторических данных - текущий бар.

2. С четвёрки на пятёрку переписываю.

3. Ну потому, что в Buffer[] записываются данные в цикле от rates_total-1 до 0. Если не делать его как таймсерию, то отображение будет на графике задом-наперёд.

4. Копирую в один момент времени одно значение, соответствующее данным бара i заданного тф.

ЗЫ. Смотри как лаконично и легко получать данные не текущего тф в mql4, и они получаются успешно, и всё работает как задумано. И как странно получать данные индикатора в mql5, и они не получаются никак - всегда ошибка 4806 если в функцию получения данных АО передать таймфрейм, не соответствующий таймфрейму графика, на котором работает индикатор.

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

Т.е., всегда, в любой момент времени, как бы мы не переключали тф, хэндл создаётся с нужным (выбранным в настройках) тф. Но данные АО можно получить лишь при совпадении выбранного тф в настройках и текущего тф. Если они не совпадают, то всегда возвращается ноль из функции.

Вопрос: ПОЧЕМУ?

1. Просто уточнение. Теперь понятно что говорим об одном и том-же.

2. Это я понял, но не соглашусь что для этого обязательно переворачивать массивы. Разве обязательно иметь один индикатор для двух терминалов??? Почти так-же как сделать 2в1 косу и топор.

3. Buffer[] как я понял используется приёмником в функции CopyBuffer() для получения всего 1 значения индикатора.

4. На самое главное ты не обратил внимания. Начало копирования значения индикатора надо определять не индексом бара, а временем i-того бара.

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