Обновление массива Rates с каждым новым баров. Или как добавить в массив только последний бар. - страница 4

 
Vladislav Boyko #:

Оно то все хорошо и правда, но это сферическое копирование в вакууме.

Реализация боевого/надежного варианта добавит ~5 экранов кода и это не сделаешь на коленке.

Опять-же, даже результат выполнения Bars() нужно обрабатывать.

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

Приведите тестовый пример, который не копирует запрошенные последние бары. Это будет баг либо в терминале, либо в MQL5.

 
Vladislav Boyko #:

Оно то все хорошо и правда, но это сферическое копирование в вакууме.

Реализация боевого/надежного варианта добавит ~5 экранов кода и это не сделаешь на коленке.

Опять-же, даже результат выполнения Bars() нужно обрабатывать.

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

У вас есть

  1. Время открытия нулевого бара на прошлой проверке,
  2. Время открытия нулевого бара на текущей проверке.
  3. Если время отличается - это новый бар.

У Вас есть

  1. Количество баров истории на прошлой проверке,
  2. Количество баров истории на текущей проверке.
  3. Разница между 1 и 2 скажет сколько баров нужно дописать в массив.

Где тут пять страниц кода?


 
Stanislav Korotky #:

Приведите тестовый пример, который не копирует запрошенные последние бары. Это будет баг либо в терминале, либо в MQL5.

Я не в состоянии пока в силу недостаточного опыта на MQL5.

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

(я не критикую ваш код, я просто акцентирую внимание на том, что, по моему мнению, при реализации надежного варианта нужно будет много всего предусмотреть и обработать)

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Обновление массива Rates с каждым новым баров. Или как добавить в массив только последний бар.

Vladislav Boyko, 2024.01.22 15:57

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

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

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

А если между вызовом Bars и копированием самих котировок придет тик, который сформирует новый бар (привет, паранойя)? Получается, при добавлении бара нужно копировать на 1 бар больше, что бы убедится потом, что все правильно добавили.

Довольно громоздкая хреновина получится с учетом всего этого.


Artyom Trishkin #:

У вас есть

  1. Время открытия нулевого бара на прошлой проверке,
  2. Время открытия нулевого бара на текущей проверке.
  3. Если время отличается - это новый бар.

У Вас есть

  1. Количество баров истории на прошлой проверке,
  2. Количество баров истории на текущей проверке.
  3. Разница между 1 и 2 скажет сколько баров нужно дописать в массив.

Где тут пять страниц кода?

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

 
Stanislav Korotky #:

Я так и предлагал - запрашивать только новые бары, добавлять в общий массив.

А ваш пример с ошибкой, поэтому вызов просто не работает (не потребляет ни времени, ни памяти):

Вместо этого должно быть:

Спасибо. Вот уж лоханулся, по полной. Но и вы не сразу это заметили. Хоть что-то в оправдание…:)))

 
Artyom Trishkin #:

У вас есть

  1. Время открытия нулевого бара на прошлой проверке,
  2. Время открытия нулевого бара на текущей проверке.
  3. Если время отличается - это новый бар.

У Вас есть

  1. Количество баров истории на прошлой проверке,
  2. Количество баров истории на текущей проверке.
  3. Разница между 1 и 2 скажет сколько баров нужно дописать в массив.

Где тут пять страниц кода?


Артём, в обоих частях есть засады :-) может прийти больше 1-го новых баров (например связь затыкалась), могут прийти некорректные данные, может прийти докачка/исправление истории. Если включая 0-й бар то незабывать копировать 2(!) бара (последний перезаписывать и новый добавлять)

не 5-ть конечно, но довольно кропотливо..Правда не разу не испытывал потребности держать полную копию Rates в отдельном массиве. В базы и эксель синхронизировал, поэтому уверенно - там есть подводные камни

 
Maxim Kuznetsov #:
не 5-ть конечно, но довольно кропотливо

5 много экранов это промышленное решение.

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

В OnTick этот менеджер сам обновляет все графики (читай классы котировок) и если что пошло не так, то возводит флаг ошибки.

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

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

Основной прикол, что даже учитывая прямой доступ в четверке, все равно громоздко получается. А в пятерке пойди еще скопируй правильно
 
Anton Novokhatskii:
CopyRates

// по ходу чо та тут шибко заморочились над поиском ответа на простейший вопрос ;)))))

решение в пару строк всего по сути

добавляйте в конец массива один бар, и пользуйтесь 

(ArrayResize)

 
Stanislav Korotky #:

Вот такой эксперт запустить в тестере в режиме по открытию баров (потому что просили именно котировки, в онлайне нужно будет добавить общую проверку на формирование нового бара - здесь для нас это делает тестер), дважды: один раз параметр Batch равный true, второй раз - false.

Вот какие замеры получаем за 2023 год:

Это только по поводу быстродействия.

Насчет засечки последнего скопированного бара - нетрудно это реализовать и не займет ни времени, ни ресурсов (только одну переменную с datetime последнего бара сохранять и отправлять в CopyRates).

Чтобы не менять постоянного направления индексации, лучше подобный тест сделать через класс, где есть геттеры с нужным направлением индексации. Получается чуть быстрее. И более удобно. 

class HistoryRates {
private:
   MqlRates r[];
   datetime last_time;
   int begin_from;
public:
   int size;
   HistoryRates(int start_history = 1) {
      size = 0;
      begin_from = start_history;
   };
   ~HistoryRates() {};
   void NewTick() {
      if (size <=0) { // первая закачка
         size = CopyRates(_Symbol,_Period,0,begin_from,r);
         if (size>0) last_time = r[size-1].time;
      } else {
         MqlRates r0[1];
         int i = CopyRates(_Symbol,_Period,0,1,r0);
         if (i==1) if (r0[0].time!=last_time) { // новый бар
               r[ArrayResize(r,++size,size<10000?size*2:10000)-1] = r0[0];
               last_time =r0[0].time;
            } else r[size-1] = r0[0]; // новый тик без нового бара
      }
   };
   void NewTick2() { // более продвинутая, но чуть медленная реализация, не претендующая на конечное использование, т.к. необходимо первоначальную закачку данных реализовывать через таймер.
      if (size <=0) { // первая закачка
         size = CopyRates(_Symbol,_Period,0,begin_from,r);
         if (size>0) last_time = r[size-1].time;
      } else {
         MqlRates r0[];
         int i = CopyRates(_Symbol,_Period,last_time,TimeCurrent(),r0);
         if (i>0) if (r0[0].time!=last_time)
               Print("Дырка в истории. Необходима программная реализация данной ситуации");
         if (i==1) r[size-1] = r0[0];  // новый тик без нового бара
         else if (i==2) {
            r[ArrayResize(r,++size,10000)-1] = r0[1];  // новый бар
            last_time =r0[1].time;
         } else if (i>2) { // новые бары
            size = ArrayResize(r,size+i, 2*(size+i));
            ArrayCopy(r,r0,size-i-1,0);
            last_time =r0[i-1].time;
         }
      }
   };
   MqlRates GetBarAsSeries(int i) { // Геттер по индексу, когда самый новый бар нулевой
      if (i>=0 && i<size) return r[size-1-i];
      else {
         Print("Index error...");
         return r[size-1];
      }
   }
   MqlRates GetBarAsNotSeries(int i) { // Геттер по индексу, когда самый старый бар нулевой
      if (i>=0 && i<size) return r[i];
      else {
         Print("Index error...");
         return r[size-1];
      }
   }
};


результат через класс:

2024.02.05 18:15:58.115 Core 01 EURUSD,H1: history cached from 2022.01.03 00:00
2024.02.05 18:15:58.115 Core 01 EURUSD,H1 (MetaQuotes-Demo): generating based on real ticks
2024.02.05 18:15:58.115 Core 01 EURUSD,H1: testing of Experts\TestActualHistory.ex5 from 2023.01.01 00:00 to 2024.01.01 00:00 started with inputs:
2024.02.05 18:15:58.115 Core 01   Batch=false
2024.02.05 18:15:58.115 Core 01 EURUSD : real ticks begin from 2023.01.02 00:00:00
2024.02.05 18:15:58.115 Core 01 final balance 10000.00 USD
2024.02.05 18:15:58.115 Core 01 2023.12.29 23:58:43   Rates: 6229
2024.02.05 18:15:58.115 Core 01 2023.12.29 23:58:43   Rates from class HistoryRates: 12434
2024.02.05 18:15:58.115 Core 01 EURUSD,H1: 21297131 ticks, 6207 bars generated. Test passed in 0:00:01.355.

результат через смену индексаций:

2024.02.05 18:08:58.959 Core 01 EURUSD,H1: history cached from 2022.01.03 00:00
2024.02.05 18:08:58.959 Core 01 EURUSD,H1 (MetaQuotes-Demo): generating based on real ticks
2024.02.05 18:08:58.959 Core 01 EURUSD,H1: testing of Experts\TestActualHistory.ex5 from 2023.01.01 00:00 to 2024.01.01 00:00 started with inputs:
2024.02.05 18:08:58.959 Core 01   Batch=false
2024.02.05 18:08:58.959 Core 01 EURUSD : real ticks begin from 2023.01.02 00:00:00
2024.02.05 18:08:58.959 Core 01 final balance 10000.00 USD
2024.02.05 18:08:58.959 Core 01 2023.12.29 23:58:43   Rates: 21303359
2024.02.05 18:08:58.959 Core 01 2023.12.29 23:58:43   Rates from class HistoryRates: 0
2024.02.05 18:08:58.959 Core 01 EURUSD,H1: 21297131 ticks, 6207 bars generated. Environment synchronized in 0:00:00.018. Test passed in 0:00:01.930.



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

void OnTick() {
   static bool once = false;
   static MqlRates rates[];
   ResetLastError();
   if(Batch || !once) {
      CopyRates(_Symbol, _Period, 0, Bars(_Symbol, _Period), rates);
      ArraySetAsSeries(rates, true);
      once = true;
   } else {
      static MqlRates r[1];
      datetime last_open_time = rates[0].time;
      CopyRates(_Symbol, _Period, 0, 1, r);
      ArraySetAsSeries(rates, false);
      if (last_open_time!=r[0].time) PUSH(rates, r[0]);
      else rates[ArrayRange(rates, 0)-1] = r[0];
      ArraySetAsSeries(rates, true);
      //hr.NewTick();
   }
   size = ArraySize(rates);
   // rates[0] - latest bar
   // ... actual processing
}


2024.02.05 18:23:00.193 Core 01 EURUSD,H1: history cached from 2022.01.03 00:00
2024.02.05 18:23:00.193 Core 01 EURUSD,H1 (MetaQuotes-Demo): generating based on real ticks
2024.02.05 18:23:00.193 Core 01 EURUSD,H1: testing of Experts\TestActualHistory.ex5 from 2023.01.01 00:00 to 2024.01.01 00:00 started with inputs:
2024.02.05 18:23:00.193 Core 01   Batch=false
2024.02.05 18:23:00.193 Core 01 EURUSD : real ticks begin from 2023.01.02 00:00:00
2024.02.05 18:23:00.193 Core 01 final balance 10000.00 USD
2024.02.05 18:23:00.193 Core 01 2023.12.29 23:58:43   Rates: 12435
2024.02.05 18:23:00.193 Core 01 2023.12.29 23:58:43   Rates from class HistoryRates: 0
2024.02.05 18:23:00.193 Core 01 EURUSD,H1: 21297131 ticks, 6207 bars generated. Environment synchronized in 0:00:00.023. Test passed in 0:00:01.359.
Файлы: