Тестируем 'CopyTicks' - страница 42

 
1702 - найденные косяки CopyTicks исправили!
 

После удачного вызова CopyTicks в оффлайне GetLastError возвращает 4403.

 
Желание получить все тики кастомного символа таким образом вызывает Out of memory
CopyTicks(Symb, Ticks, COPY_TICKS_ALL, 0, UINT_MAX); // out of memory


Сделаю через CopyTicksRange, но поведение CopyTicks видится правильным изменить.

 
Иногда CopyTicksRange вызывает закачку БАРОВ с бородатого года: 2003.hcc и т.д.
 
CopyTicksRange на кастомном символе возвращает ноль. CopyTicks - норм.
 

CopyTicks (build 1881) возвращает более старые данные, чем были запрошены, если запрашивать не свежие тики. Т.е. возвращает данные старее, чем параметр from. Баг плавающий - проявляется в разное время, поэтому написал небольшой код, который воспроизводит. Запускал в тестере на EURUSD H1, 2017.08.01 - 2018.08.01.

void OnTick()
{
   static datetime lastActivityTime = D'2017.08.01';   
   static MqlTick ticks[2000];
   static const uint requestedCount = 2000;
   datetime dt[1];
   CopyTime(NULL, PERIOD_CURRENT, 0, 1, dt);
   if (lastActivityTime >= dt[0]) {
      return;
   }
   lastActivityTime = TimeCurrent();
   
   int zero = 0;
   int idx = 0;
   do {
      ++idx;
      CopyTime(NULL, PERIOD_CURRENT, idx, 1, dt);
      if (dt[0] <= D'2017.08.01') break;
      Print("dt[0]=", dt[0]);
      ulong from = 1000 * dt[0];
      int cnt = CopyTicks(Symbol(), ticks, COPY_TICKS_INFO, from, requestedCount);
      if (cnt < 1) {
         Print("Error in CopyTicks");
         return;
      }
      Print("cnt=", cnt);
      for (int i = 0; i < cnt; ++i) {
         if (ticks[i].time_msc < from) {
            Print("ERROR: i=", i, ", ticks[i].time_msc=", ticks[i].time_msc, " (", ticks[i].time, ")");
            i = i / zero;
         }
      }
      Print("done");
   } while(true);
}

Вот выхлоп:

2018.10.17 21:31:26.221 2017.08.01 12:00:00   dt[0]=2017.08.01 03:00:00

2018.10.17 21:31:26.221 2017.08.01 12:00:00   cnt=2000

2018.10.17 21:31:26.221 2017.08.01 12:00:00   ERROR: i=0, ticks[i].time_msc=1501552175606 (2017.08.01 01:49:35)

Т.е. запросили мы с 03 часов, а получили с 01:49. В реальных условиях разница была больше месяца.

 
Вопрос к бывалым. Какие потенциальные ошибки могут быть при таком способе получения свежих тиков?
input datetime inFrom = __DATETIME__;

// Свежие тики с последнего вызова
int GetFreshTicks( MqlTick &Ticks[] )
{
  static long LastTime = 0;
  static int LastAmount = 0;
  
  ArrayFree(Ticks);

  int Size = CopyTicksRange(_Symbol, Ticks, COPY_TICKS_INFO, LastTime ? LastTime : (long)inFrom * 1000);
  
  if (Size > LastAmount)
  {
    LastTime = Ticks[Size - 1].time_msc;
    int NewLastAmount = 1;
    
    for (int i = Size - 2; (i >= LastAmount) && (Ticks[i].time_msc == LastTime); i--)
      NewLastAmount++;
      
    if (ArrayRemove(Ticks, 0, LastAmount))
      Size -= LastAmount;
      
    LastAmount = NewLastAmount;
  }
  else
    Size = ArrayResize(Ticks, 0);
  
  return(Size);
}

void OnTick()
{
  MqlTick Ticks[];
  
  if (GetFreshTicks(Ticks))
    ArrayPrint(Ticks);
}
 
fxsaber:
Вопрос к бывалым. Какие потенциальные ошибки могут быть при таком способе получения свежих тиков?

Порядок тиков с одинаковым временем не гарантирован, вроде бы.

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

Тики в реальном времени

Andrey Khatimlianskii, 2020.01.31 14:40

Кстати, по корректному сбору тиков была прекрасная статья Василия Соколова. Там подробно разбирается процесс синхронизации (которого у меня нет, из-за чего иногда принтуются одинаковые тики):

Но функция CopyTiks не позволяет запрашивать N последних тиков. Вместо этого она предоставляет все тики, пришедшие с указанного момента времени. Это усложняет задачу. Мы должны выполнить запрос, получить массив тиков и сравнить его с массивом тиков, полученным на предыдущем обновлении. При этом мы выясним, какие из вновь пришедших тиков не входили в "прошлую поставку", то есть являются новыми. Но сравнивать тики между собой напрямую невозможно, просто потому что видимых различий между ними может вообще не быть. Например, обратимся к нижеприведенной таблице сделок:

Рис. 5. Таблица всех сделок с примером одинаковых сделок.

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

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


Пишем скальперский стакан цен на основе графической библиотеки CGraphic
Пишем скальперский стакан цен на основе графической библиотеки CGraphic
  • www.mql5.com
Именно с этой, улучшенной и дополненной версией мы и начнем работать, чтобы постепенно превратить ее в скальперский стакан цен. Краткий обзор графической библиотеки CPanel Созданию пользовательских интерфейсов в MQL5 посвящено много статей. Среди них особенно выделяется серия Анатолия Кажарского "Графические интерфейсы", после которой сложно...
 
Andrey Khatimlianskii:

Порядок тиков с одинаковым временем не гарантирован, вроде бы.

Если речь про группы тиков, то, вроде, в коде с этим все в порядке.

 
Тиковый кеш не сбрасывается.
#define TOSTRING(A) " " + #A + " = " + (string)(A)

MqlTick Ticks[];

void OnInit()
{
  Print(__FUNCTION__ + TOSTRING(TerminalInfoInteger(TERMINAL_MEMORY_USED)) + TOSTRING(MQLInfoInteger(MQL_MEMORY_USED))); // Распечатываем начальное состояние памяти.
  
  CopyTicksRange(_Symbol, Ticks, COPY_TICKS_INFO, D'2020.01.01' * 1000); // Получили историю тиков для инициализации по ней советника.
}

void OnTick()
{
  const int Size = ArraySize(Ticks);
  
  if (Size)
  {
    const long BeginTime = Ticks[Size - 1].time_msc;
    
    ArrayFree(Ticks);
    
    CopyTicksRange(_Symbol, Ticks, COPY_TICKS_INFO, BeginTime); // Получаем свежие тики без пропусков, чтобы гнать по ним советник.
  }
  
  Print(__FUNCTION__ + TOSTRING(TerminalInfoInteger(TERMINAL_MEMORY_USED)) + TOSTRING(MQLInfoInteger(MQL_MEMORY_USED))); // Распечатываем текущее состояние памяти.
}


Результат (запускать на холодную - сразу после старта Терминала).

OnInit TerminalInfoInteger(TERMINAL_MEMORY_USED) = 395 MQLInfoInteger(MQL_MEMORY_USED) = 1
OnTick TerminalInfoInteger(TERMINAL_MEMORY_USED) = 446 MQLInfoInteger(MQL_MEMORY_USED) = 1
OnTick TerminalInfoInteger(TERMINAL_MEMORY_USED) = 446 MQLInfoInteger(MQL_MEMORY_USED) = 1
OnTick TerminalInfoInteger(TERMINAL_MEMORY_USED) = 446 MQLInfoInteger(MQL_MEMORY_USED) = 1


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

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