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

 

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

2017.04.17 00:27:51.705 Core 1  GOLD-6.17: generate 48836 ticks in 0:00:00.000, passed to tester 48984 ticks
2017.04.17 00:27:51.705 Core 1  GOLD-9.17: generate 3184 ticks in 0:00:00.000, passed to tester 35192 ticks

а все из-за чего

2017.04.17 00:22:16.126 Trade   2017.04.07 10:00:00   deal performed [#247 buy 1.00 GOLD-9.17 at 0.0]

теперь прогоняем еще раз

2017.04.17 00:31:51.123 Core 1  GOLD-6.17: passed to tester 48984 ticks
2017.04.17 00:31:51.123 Core 1  GOLD-9.17: passed to tester 35192 ticks

и результат теста абсолютно другой


Файлы:
 

@Renat Fatkhullin, приведите веские доказательства, что тестер точен и готов к употреблению.

Где четкие критерии проверки годности тестера? Где 100%-я воспроизводимость и совпадение с эталоном?

Где железная аргументация своих слов?


Предлагаю выложить в паблик ВСЕ ваши проверочные эксперты для тестера. Предлагаю платить людям, которые пишут советники, заваливающие тестер. И включать их в проверочную базу после корректировки тестера.


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

 
kaus_bonus:

с выходом билда 1583 вернемся к вопросу несовпадения колва тиков в истории с колвом тиков затем в тестере на малоликвидных биржевых тикерах


Вам в сервисдеске ответили, что проблема решена? Последний ответ был "разбираемся".

Проблема ещё не решена. К сожалению, не всё зависит от разработчиков, часть проблемы зависит от того, какого качества тики выложены на торговом сервере.

Разбираемся. Подождите, пожалуйста.

 

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

//+------------------------------------------------------------------+
//|                                                    TestTicks.mq5 |
//|                        Copyright 2017, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property script_show_inputs
//--- input parameters
input datetime InpBeginDate=D'2017.01.01 0:00:00';
input datetime InpEndDate=D'2017.12.31 0:00:00';
input bool     InpFullLogs=false;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   MqlRates rates[];
   MqlTick  ticks[];
   datetime begin_date,end_date=(datetime)SymbolInfoInteger(_Symbol,SYMBOL_TIME);
   datetime prev_date,cur_date; 
   int      cnt_rates,cnt_ticks;
   int      i,j,book_depth=(int)SymbolInfoInteger(_Symbol,SYMBOL_TICKS_BOOKDEPTH);
   int      bars_absent=0,bars_absent_wrong=0,tickminutes_absent=0;
   int      bars_volume=0,bars_highlow=0;
   string   str_ticks=(book_depth>0)?" bid/ask":" last/volume";
//--- настроим даты
   if(InpEndDate<end_date && InpEndDate>InpBeginDate)
      end_date=InpEndDate;
   end_date/=60;
   end_date*=60;
   end_date--;
   if(InpBeginDate>=end_date)
      begin_date=end_date-24*3600;
   else
     {
      begin_date=InpBeginDate;
      begin_date/=60;
      begin_date*=60;
     }
//---
   cnt_rates=CopyRates(_Symbol,PERIOD_M1,begin_date,end_date,rates);
   Print(cnt_rates," M1 bars from ",begin_date," to ",end_date);
   for(i=0; i<cnt_rates; i++)
     {
      if(IsStopped())
        {
         Print("stopped on ",i," at ",rates[i].time);
         break;
        }
      if(i==0)
         prev_date=begin_date-60;
      else
         prev_date=rates[i-1].time;
      cur_date=rates[i].time;
      Comment(cur_date,"  ",i+1," / ",cnt_rates,"\n",tickminutes_absent,"  ",bars_volume,"  ",bars_highlow,"  ",bars_absent_wrong);
      //--- отсутствуют минутки
      if(cur_date-prev_date>60)
        {
         prev_date+=60;
         cnt_ticks=CopyTicksRange(_Symbol,ticks,COPY_TICKS_ALL,prev_date*1000,cur_date*1000-1);
         if(cnt_ticks>0)
           {
            datetime next_minute=(ticks[0].time/60+1)*60;
            int absent=1;
            int trades=0,infos=0;
            for(j=0; j<cnt_ticks; j++)
              {
               if((ticks[j].flags&(TICK_FLAG_BID|TICK_FLAG_ASK))!=0)
                  infos++;
               if((ticks[j].flags&(TICK_FLAG_LAST|TICK_FLAG_VOLUME))!=0)
                  trades++;
               //--- считаем реально отсутствующие минутки
               if(ticks[j].time>=next_minute)
                 {
                  next_minute=(ticks[j].time/60+1)*60;
                  absent++;
                 }
              }
            //--- тики не соответствуют барам
            if((book_depth<1 && infos>0) ||
               (book_depth>0 && trades>0))
              {
               Print(cnt_ticks," ticks between ",prev_date," and ",cur_date," (",absent," M1 bars absent)");
               if(InpFullLogs)
                  for(j=0; j<cnt_ticks; j++)
                    {
                     Print(ticks[j].time,".",IntegerToString(ticks[j].time_msc%1000,3,'0'),
                           " Bid=",DoubleToString(ticks[j].bid,_Digits),
                           " Ask=",DoubleToString(ticks[j].ask,_Digits),
                           " Last=",DoubleToString(ticks[j].last,_Digits),
                           " Flags=",ticks[j].flags,
                           " Volume=",ticks[j].volume);
                    }
               //--- счётчик отсутствующих баров
               bars_absent_wrong+=absent;
              }
            else
              {
               if(InpFullLogs)
                  Print(cnt_ticks,str_ticks," ticks between ",prev_date," and ",cur_date," (",absent," M1 bars)");
               //--- счётчик отсутствующих баров
               bars_absent+=absent;
              }
           }
        }
      cnt_ticks=CopyTicksRange(_Symbol,ticks,COPY_TICKS_ALL,cur_date*1000,(cur_date+60)*1000-1);
      if(cnt_ticks<=0 && !IsStopped())
        {
         if(InpFullLogs)
            Print("Ticks absent for M1 ",cur_date," last_error=",GetLastError());
         tickminutes_absent++;
        }
      //--- проверим качество тиков
      if(cnt_ticks>0)
        {
         double high_trades=0.0,low_trades=0.0;
         double high_infos=0.0,low_infos=0.0;
         int    trades=0,infos=0;
         //--- сначала соберём статистику
         for(j=0; j<cnt_ticks; j++)
           {
            if((ticks[j].flags&(TICK_FLAG_BID|TICK_FLAG_ASK))!=0)
              {
               if(ticks[j].bid>0.0)
                 {
                  if(high_infos==0.0)
                     high_infos=ticks[j].bid;
                  else
                    {
                     if(high_infos<ticks[j].bid)
                        high_infos=ticks[j].bid;
                    }
                  if(low_infos==0.0)
                     low_infos=ticks[j].bid;
                  else
                    {
                     if(low_infos>ticks[j].bid)
                        low_infos=ticks[j].bid;
                    }
                 }
               infos++;
              }
            if((ticks[j].flags&(TICK_FLAG_LAST|TICK_FLAG_VOLUME))!=0)
              {
               if(ticks[j].last>0.0)
                 {
                  if(high_trades==0.0)
                     high_trades=ticks[j].last;
                  else
                    {
                     if(high_trades<ticks[j].last)
                        high_trades=ticks[j].last;
                    }
                  if(low_trades==0.0)
                     low_trades=ticks[j].last;
                  else
                    {
                     if(low_trades>ticks[j].last)
                        low_trades=ticks[j].last;
                    }
                 }
               trades++;
              }
           }
         //--- потом сверим с минутным баром
         double point=pow(10.0,-_Digits);
         double high,low;
         int    volume;
         bool   error=false;
         //--- это биржевой инструмент?
         if(book_depth>0)
           {
            high=high_trades;
            low=low_trades;
            volume=trades;
           }
          else
           {
            high=high_infos;
            low=low_infos;
            volume=infos;
           }
         if(MathAbs(low-rates[i].low)>=point)
           {
            error=true;
            bars_highlow++;
           }
         else
           {
            if(MathAbs(high-rates[i].high)>=point)
              {
               error=true;
               bars_highlow++;
              }
            else
              {
               if(volume!=rates[i].tick_volume)
                 {
                  error=true;
                  bars_volume++;
                 }
              }
           }
         //--- надо вывести сообщение об ошибке
         if(error && InpFullLogs)
            Print(rates[i].time," ticks (",DoubleToString(high,_Digits)," ",DoubleToString(low,_Digits)," ",volume,
                  ") mismatch with M1 bar (",DoubleToString(rates[i].high,_Digits)," ",DoubleToString(rates[i].low,_Digits)," ",rates[i].tick_volume,")");
        }
     }
//---
   if(tickminutes_absent>0)
      Print("real ticks absent for ",tickminutes_absent," M1 bars");
   if(bars_volume>0)
      Print("tick volumes not matched for ",bars_volume," M1 bars");
   else
      Print("all the tick volumes matched for all M1 bars");
   if(bars_highlow>0)
      Print("tick prices not matched for ",bars_volume," M1 bars");
   else
      Print("all the tick prices matched for all M1 bars");
   if(bars_absent_wrong>0)
      Print(bars_absent_wrong," M1 bars absent in total while real ticks present");
//--- тут всё нормально, есть тики, которые не формируют бары, поэтому баров и не должно быть
   if(bars_absent>0 && InpFullLogs)
      Print(bars_absent," M1 bars absent while",str_ticks," ticks present");
//--- очистим коммент
   Comment("");
  }
//+------------------------------------------------------------------+
Файлы:
TestTicks.mq5  17 kb
 
Slawa:

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

Спасибо. Небольшое замечание
book_depth=(int)SymbolInfoInteger(_Symbol,SYMBOL_TICKS_BOOKDEPTH);
Так определять принадлежность к forex/бирже нельзя - многие транслируют forex-стакан.


У меня на Metaquotes-Demo такой результат

Test2 (Si-6.17,M1)      6 M1 bars from 2017.04.17 00:00:00 to 2017.04.17 10:05:59
Test2 (Si-6.17,M1)      9 bid/ask ticks between 2017.04.17 00:00:00 and 2017.04.17 10:00:00 (9 M1 bars)
Test2 (Si-6.17,M1)      all the tick volumes matched for all M1 bars
Test2 (Si-6.17,M1)      all the tick prices matched for all M1 bars
Test2 (Si-6.17,M1)      9 M1 bars absent while bid/ask ticks present

Это нормально?

 
Slawa:

Вам в сервисдеске ответили, что проблема решена? Последний ответ был "разбираемся".

Проблема ещё не решена. К сожалению, не всё зависит от разработчиков, часть проблемы зависит от того, какого качества тики выложены на торговом сервере.

Разбираемся. Подождите, пожалуйста.


я не в претензии.

в СД заявка по другому поводу - несовпадение тиков.

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

 
kaus_bonus:


я не в претензии.

в СД заявка по другому поводу - несовпадение тиков.

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

Это - оттуда же. Проблема с тиками - одна.

Раньше описываемого Вами случая не было? Не было.

Откуда взялся? В процессе решения проблемы совпадения тиков

 
fxsaber:
Спасибо. Небольшое замечаниеТак определять принадлежность к forex/бирже нельзя - многие транслируют forex-стакан.



При чём здесь биржа - не биржа?

если транслируется стакан, значит бары строятся по ластам.

 
Slawa:

При чём здесь биржа - не биржа?

если транслируется стакан, значит бары строятся по ластам.

Какие ласты на форексе? Откройте демо здесь FIBOGroup-MT5 Server.
 
fxsaber:
Какие ласты на форексе? Откройте демо здесь FIBOGroup-MT5 Server.

Если транслируется стакан, то бары строятся по ластам, в противном случае по бидам

Понял Ваш вопрос. С недавнего времени, в связи с расширением тиковой функциональности, существуют настройки - "всегда строить по ластам", "всегда строить по бидам", "строить в зависимости от трансляции стакана".

В представленном скрипте первые две настройки никак не учитываются. Считается "по-старому"

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