Библиотеки: OnTickMulti - страница 5

 
Stanislav Korotky #:

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

Нет. Задача всегда иметь актуальное окружение по всем символам. В моем примере в OnTimer всегда выполняется условие актуальности. Открытие позиции - только демонстрация.
 
fxsaber #:
Нет. Задача всегда иметь актуальное окружение по всем символам. В моем примере в OnTimer всегда выполняется условие актуальности. Открытие позиции - только демонстрация.

Хорошо.

Пока неясно, где реализация на событии OnTickMulti "теряет" тик или иными словами, как получается, что SymbolInfoTick возвращает не те цены, что ожидались (я ранее предположил, что вероятно есть задержка из-за диспетчеризации кастом-событий из индикатора) - я бы потестировал без индикатора-шпиона для чистоты эксперимента - просто SymbolInfoTick/CopyTicks по всем символам из обычного OnTick хотя б для того же таймстемпа.

Что же касается OnTimer, он у меня вызывает вопросы (поправьте, в чем ошибаюсь):

void OnTimer()
{
  if (TimeMsc++ == 1759280400081) // 2025.10.01 01:00:00.081
    PositionOpen();
}

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

Кроме того, и начальная синхронизация (инициализация счетчика) - не 100% чиста, имхо.

ulong TimeMsc = TimeTradeServer() * 1000 + (inTimer && EventSetMillisecondTimer(1));

Допустим в тестере действительно время сервера начинается без миллисекунд, но как такой код заработает в онлайн? И почему мы прибавляем 1 миллисекунду? Я б все же брал серверное время из тиков.

Это не придирки, а просто сомнения по поводу "всегдашнего выполнения условия актуальности".

 
Stanislav Korotky #:

простой инкремент счетчика не гарантирует сохранение изначальной синхронизации

В Тестере гарантия.

но как такой код заработает в онлайн?

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

И почему мы прибавляем 1 миллисекунду?

Потому что самый первый OnTimer вызовется через заданный интервал, а не сразу.

 
Stanislav Korotky #:

Пока неясно, где реализация на событии OnTickMulti "теряет" тик или иными словами, как получается, что SymbolInfoTick возвращает не те цены, что ожидались

OnTickMulti точно не при делах, т.к. сама штатная SymbolInfoTick одного из символов возвращает правильный тик, а для других символов - нет.


Причина только в последовательности подачи тиков.

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

Библиотеки: OnTickMulti

fxsaber, 2025.09.30 09:24

Тики с одинаковым временем не приходят одновременно. Все последовательно. И если тик по EURUSD c бОльшим временем пришел первым, то на этот момент ничего не известно о тике с таким же временем по GBPUSD, который придет вторым. Поэтому на момент прихода первого тика EURUSD, второго тика GBPUSD просто нет, а есть данные предыдущего тика GBPUSD.
 
fxsaber #:

Потому что самый первый OnTimer вызовется через заданный интервал, а не сразу.

Придумал быстрый способ актуализации всех данных.

#include <fxsaber\OnTickMulti\OnTickMulti.mqh> // https://www.mql5.com/ru/code/47647

#include <MT4Orders.mqh> // https://www.mql5.com/ru/code/16006

void PositionOpen()
{
  for (uint i = ArraySize(OnTickMultiObject.Symbols); (bool)i--;)
  {
    const string SymbName = OnTickMultiObject.Symbols[i];
    
    OrderSend(SymbName, OP_BUY, 1, SymbolInfoDouble(SymbName, SYMBOL_ASK), 0, 0, 0);
  }
}

long CurrentTime = 0;

// Мультисимвольный OnTick.
void OnTickMulti( const string &Symb, const uint &Index )
{  
  MqlTick Tick;
  
  if (SymbolInfoTick(Symb, Tick) && (Tick.time_msc > CurrentTime))
  {
    CurrentTime = Tick.time_msc;
    
    EventSetMillisecondTimer(1);
  }  
}

void OnTimer()
{
  if (CurrentTime == 1759280400081) // 2025.10.01 01:00:00.081
    PositionOpen();
    
  EventKillTimer();
}

Такой механизм позволяет даже в обычном режиме (моновалютник без OnTickMulti) работать только с актуальными данными. Например, по EURUSD есть несколько тиков с одинаковым временем. Актуализация позволяет работать с актуальным тиком - последним в этой последовательности.


ЗЫ Это еще одна причина создавать кастомные символы - закидывать в историю только последний тик из последовательностей с одинаковым временем. В таком случае в моновалютном режиме будет всегда соблюдаться актуализация.

Библиотеки: TicksShort
Библиотеки: TicksShort
  • 2025.09.26
  • www.mql5.com
Статьи и техническая библиотека по автоматическому трейдингу: Библиотеки: TicksShort
 
Rorschach #:

Но ведь по ценам открытия тоже самое, хотя время открытия по всем символам одинаковое.

И из-за такого поведения ряд систем невозможно нормально тестировать.

Освежил в памяти режим тестера по ценам открытия. Фишка в том, что несмотря на название, тестер генерирует в этом режиме 4 тика OHLC, а не 1 как интуитивно ожидается из названия. Из этих 4-х контрольных точек для экспертов берется только первая O и вызывается OnTick, а для индикаторов дополнительно для HLC или LHC (в зависимости от направления бара) вызывается 3 раза OnCalculate с тиками для соответствующих цен. Времена для этих трех доп.точек берутся искусственно равными последним трем секундам бара. Получается, что индикатор-шпион отправляет для символов несколько событий вместо одного. Вероятно, это надо учитывать тем, кто использует режим по ценам открытия.

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

int OnCalculate(const int rates_total, const int prev_calculated, const int, const double &price[])
{
   MqlTick tick;
   SymbolInfoTick(_Symbol, tick);
   EventChartCustom(Chart, Message, Index, (double)tick.time_msc, NULL);
  
   return rates_total;
}

И при приеме (показан случай одного доп.символа, для многих нужен массив timestamp[]!):

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   if(id == CHARTEVENT_CUSTOM + Message)
   {
      static long timestamp;
      long event = (long)dparam;
      if(event > timestamp)
      {
         OnSymbolTick((int)lparam);
         timestamp = event;
      }
   }
}

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

 
Rorschach #:
Самый простой способ синхронизации
Думал синхронизация таймсерий хорошо изученая проблема и есть готовые алгоритмы, сунулся в интернет а там пусто