Тики в реальном времени - страница 22

 
Andrey Khatimlianskii:

Посмотрите на время лога. Это все в одну мс произошло, и рядом (в эту же мс) куча ОнБук-ов.

Можно посчитать все события счетчиками, но даже визуально видно, что ОнБуков больше.

Андрей, там числа это зафиксированные микросекунды при срабатывании ОнФункций, а принтуется все вместе потом из массива. ОнБуков всего в итоге может и больше - посчитаю, но непонятно почему в очереди они пропускают вперёд ОнТики чтоли. Или не каждому ОнТик соответствует ОнБук?
 
prostotrader:


Вы вроде писали, что используете Async ордера?
Стало интересно, каким алгоритмом вы контролируете исполнение сделок?

 
Aleksey Mavrin:
Андрей, там числа это зафиксированные микросекунды при срабатывании ОнФункций, а принтуется все вместе потом из массива. ОнБуков всего в итоге может и больше - посчитаю, но непонятно почему в очереди они пропускают вперёд ОнТики чтоли. Или не каждому ОнТик соответствует ОнБук?

добро пожаловать в мир сетевых технологий ))))

самое простое под администратором запустить:   Netstat - a -b

увидите порты и софт, не хочу ковыряться, но думаю сервер МТ5 асинхронно гоняет пакеты с разной инфой, которую терминал уже раскладывает по нужным "полочкам"

ЗЫ: про Print() и пропуски принтов если много за раз выводите в курсе? - пишите в файл свою инфу - так точно все и по порядку сохраните, но не забудьте файл закрыть перед закрытием. В теории и Print() в логах в файле должен быть полный, но не проверял и в целом не доверяю если много данных выводить. Тут обсуждали https://www.mql5.com/ru/forum/329730  , очень часто "пропажа принтов" обсуждается ))) - поиском

 
Aleksey Mavrin:
Андрей, там числа это зафиксированные микросекунды при срабатывании ОнФункций, а принтуется все вместе потом из массива. ОнБуков всего в итоге может и больше - посчитаю, но непонятно почему в очереди они пропускают вперёд ОнТики чтоли. Или не каждому ОнТик соответствует ОнБук?

Понял.

Ну так рядом все равно куча ОнБук-ов. С таким логом делать какие-то выводы сложно.

 
Roman:

Вы вроде писали, что используете Async ордера?
Стало интересно, каким алгоритмом вы контролируете исполнение сделок?

В OnTradeTransaction() + функция проверки, если долго нет ответа сервера.

Вообщем по magic.

Я для каждого символа, при установки советника, резервирую 65535 магиков,

а при отправке ордера, присваиваю ему уникальный магик, который никак не

не пересекается с другими инструментами. 

Вот так я присваиваю начальный магик для символа

//+------------------------------------------------------------------+
//|                                                    AutoMagic.mqh |
//|                                 Copyright 2017-2018 prostotrader |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
//version   "1.01
ulong symb_magic;
//-------------------------------------------------------------------+
// Split string function                                             |
//+------------------------------------------------------------------+
string SplitString(const string a_str,ulong &a_month,ulong &a_year)
  {
   int str_size=StringLen(a_str);
   int str_tire=StringFind(a_str, "-");
   int str_tochka=StringFind(a_str, ".", str_tire);
   if((str_tire>0) && (str_tochka>0) &&(str_size > 0))
     {
      a_month= ulong(StringToInteger(StringSubstr(a_str,str_tire+1,str_tochka-str_tire-1)));
      a_year = ulong(StringToInteger(StringSubstr(a_str,str_tochka+1,str_size-str_tochka-1)));
      if((a_month > 0) && (a_year > 0)) return(StringSubstr(a_str, 0, str_tire));
     }
   return("");
  }
//-------------------------------------------------------------------+
// Get Magic function                                                |
//+------------------------------------------------------------------+
ulong GetMagic(const string a_symbol)
{
  symb_magic = 0;
  if(SymbolSelect(Symbol(), true) == false)
  {
    Print(__FUNCTION__, ": Нет такого символа!");
    return(0);
  }
  ulong month = 0;
  ulong year = 0;
  string new_str = SplitString(a_symbol,month,year);
  if(StringLen(new_str)>0)
  {
    uchar char_array[];
    int result=StringToCharArray(new_str,char_array,0,WHOLE_ARRAY,CP_ACP);
    if(result>0)
   {
     ulong value;
     for(int i = 0; i < result - 1; i++)
     {
       value=ulong(char_array[i]);
       value<<=(56 -(i*8));
       symb_magic += value;
     }
     month<<=24;
     symb_magic += month;
     year<<=16;
     symb_magic += year;
     return(symb_magic);
   }
 }
  return(0); 
}
//-------------------------------------------------------------------+
// Is my magic function                                              |
//+------------------------------------------------------------------+
bool IsMyMagic(const ulong m_magic)
{
  if(m_magic > 0)
  {
    ulong stored_magic=symb_magic;
    stored_magic>>=16;
    ulong in_magic = m_magic;
    in_magic>>=16;
    if(in_magic == stored_magic) return(true);
  }  
  return(false);
}
//-------------------------------------------------------------------+
// Get stored magic function                                         |
//+------------------------------------------------------------------+
ulong GetStoredMagic()
{
  if(symb_magic > 0) return(symb_magic);
  return(0);  
}
//+------------------------------------------------------------------+

Magic - ulong (8 байт) Н-р

GAZR-3.12 

Байт[7] (старший байт) - "G"

Байт[6]                         - "A"

Байт[5]                         - "Z"

Байт[4]                         - "R"

Байт[3]                         - "3"

Байт[2]                        - "12"

Байт[1] и Байт[0] - резерв для магиков (65535)

В вот так перебираю магики, при отправке ордера:

  mem_magic = magic_storage + 1;
  if(magic_storage >= (magic_number + 65530)) mem_magic = magic_number;

Но это работает только для ФОРТС, т.к имена символов стандартизированы!

Добавлено

И если ордер успешно отправлен, то

запоминаю время

  if(OrderSendAsync(request, result) == true)
  {
    if((result.retcode == TRADE_RETCODE_PLACED) || (result.retcode == TRADE_RETCODE_DONE)) 
    {
      req_id = result.request_id;
      magic_storage = mem_magic;
      state = ORD_DO_SET;
      mem_time = GetMicrosecondCount();
      mem_start_time = TimeCurrent();
      SetTransCount();
    }
    else
    {
      mem_magic = 0;
      mem_time = 0;
      mem_start_time = 0;
      CheckError(result.retcode, "Place: Ордер не установлен! Причина: ", order_status, ticket);
    }
  }
mem_time = GetMicrosecondCount(); - для проверки времени задержки OnTradeTransaction
mem_start_time = TimeCurrent();   - для сужения рамок поиска в истории

А дальше (если нет ответа в OnTradeTransaction)

ticket = FindOrderBuyMagic(mem_magic, start_time);

Ну и сама функция FindOrderBuyMagic

#define TIME_DELAY    180
//+------------------------------------------------------------------+
// Expert Find order Buy Magic function                              |
//+------------------------------------------------------------------+
ulong FindOrderBuyMagic(const ulong a_magic, const datetime set_time)
{
  if(a_magic > 0)
  {
    if(IsMyMagic(a_magic) == true)
    {
      ulong cur_ticket = 0;
      for(int i = OrdersTotal() - 1; i >= 0; i--)
      {
        cur_ticket = OrderGetTicket(i);
        if(OrderSelect(cur_ticket))
        {
          if( ulong(OrderGetInteger(ORDER_MAGIC)) == a_magic) return(cur_ticket);
        }  
     }
      cur_ticket = 0;
      datetime start_time = datetime(ulong(set_time) - TIME_DELAY);
      datetime end_time = datetime(ulong(TimeCurrent()) + TIME_DELAY);    
      if(HistorySelect(start_time, end_time))
      {
        for(int i = HistoryOrdersTotal() - 1; i >= 0; i--)
        {
          cur_ticket = HistoryOrderGetTicket(i);
          if(ulong(HistoryOrderGetInteger(cur_ticket, ORDER_MAGIC)) == a_magic) return(cur_ticket);
        }
      }
    }
  }
  return(0);
}

Добавлено

"По хорошему" в автомагик в 1-ый байт нужно добавить идентификатор советника (0-255),

но мне, пока, не надо, вот и не сделал :) 

Документация по MQL5: Основы языка / Функции / Функции обработки событий
Документация по MQL5: Основы языка / Функции / Функции обработки событий
  • www.mql5.com
В языке MQL5 предусмотрена обработка некоторых предопределенных событий. Функции для обработки этих событий должны быть определены в программе MQL5: имя функции, тип возвращаемого значения, состав параметров (если они есть) и их типы должны строго соответствовать описанию функции-обработчика события. Именно по типу возвращаемого значения и по...
 
prostotrader:

В OnTradeTransaction() + функция проверки, если долго нет ответа сервера.

Спасибо, за подсказку.

 
prostotrader:

1. Второй и последующие терминалы у брокера - платные, и у меня нет стратегий, где я торгую только акциями (портфелями акций).

2. Если Вы собираетесь выводить накопленные GetMicrosecondCount(), то 

сделайте это без таймера в OnDeinit(), при выходе советника все распечатается.

киньте плз ссыль на брокера, можно в ЛК.

Интересная ветвь получилась... :-)

 
Igor Makanu:

добро пожаловать в мир сетевых технологий ))))

самое простое под администратором запустить:   Netstat - a -b

увидите порты и софт, не хочу ковыряться, но думаю сервер МТ5 асинхронно гоняет пакеты с разной инфой, которую терминал уже раскладывает по нужным "полочкам"

ЗЫ: про Print() и пропуски принтов если много за раз выводите в курсе? - пишите в файл свою инфу - так точно все и по порядку сохраните, но не забудьте файл закрыть перед закрытием. В теории и Print() в логах в файле должен быть полный, но не проверял и в целом не доверяю если много данных выводить. Тут обсуждали https://www.mql5.com/ru/forum/329730  , очень часто "пропажа принтов" обсуждается ))) - поиском

Игорь, пропажа принтов обсуждается у тех кто не додумался файл лога открыть, сто раз и сам Ринат Фаткуллин писал что в файл лога ничего не теряется. Но чтоб ваш пост не был зря :) я добавил вывод в отдельный файл, кроме того, сделал ещё и второй файл, куда вывожу немного по другому (накапливая все события в CArrayObj) чтобы обойти возможные косяки своей конструкции которая упорядочивает два массива, т.е. я пихаю всё в CArrayObj из двух массивов, потом сортирую по микросекундам и вывожу с пометкой что за событие Тик или Бук.

И да, причём здесь порты, что это даст? Я же просто тестирую очередь событий эксперта. Если пришёл тик, должны формироваться два события - ОнТик, и соотвутствующий ему ОнБук, при этом ОнБук ставится в очередь всегда, а ОнТик может пропасть, если в очереди уже есть ОнТик (так в мануале), т.е. ситуация когда друг за другом идут ОнТики без ОнБуков может быть только если  1. ОнТики проходят "без очереди" 2. есть системная задержка ОнБука, это я и хочу проверить, этим может и объясняется лаг  в секунды, ранее выявленный коллегами. Всего ОнБуков в 2+ раза больше за день вышло, но почему они отстают?  Если эта задержка из-за асинхронности пакетов и раскладывания по полочкам, возможно, но проверяю пока лишь факт дохода их в эксперт. Как протестить с учетом остальных нюансов, не задумывался пока.

Вот новый код, по открытию протестирую корректность работы и запущу на день.

з.ы. причина еще может быть в том: если Тик прошёл по тем же ценам без изменения стакана - ОнБУк не формируется?  я не спец в биржевой торговле, кто подскажет. Я думал ОнТик всегда вызывает ОнБук.

//+------------------------------------------------------------------+
//|                                                   TestOnBook.mq5 |
//|                                           Copyright 2019, Allex@ |
//|                                                 alex-all@mail.ru |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Allex@"
#property link      "alex-all@mail.ru"
#property version   "1.00"
#include <Allex\Logger.mqh>
#include <Arrays\ArrayObj.mqh>
//---
bool is_book;
enum ENUM_BOOK_OR_TICK
{
        USE_BOOK,       // Use OnBookEvent
        USE_TICK        // Use OnTick
};
class CMcsOn: public CObject
{
public:
ulong mcs;
ENUM_BOOK_OR_TICK Ontype;
CMcsOn(ulong m, ENUM_BOOK_OR_TICK t):mcs(m),Ontype(t){};
int       Compare(const CObject*Object,const int mode=0) const
     {
      const CMcsOn* obj1=dynamic_cast<const CMcsOn*>(Object);
      CMcsOn* obj=(CMcsOn*)(obj1);
      if(!obj)return 0;
      return (mcs-obj.mcs);
      }
};
input ENUM_BOOK_OR_TICK Mode = USE_BOOK;
input int   SecForPrint =  3600;
//---
ulong TimeArrayBook[65536];
ulong TimeArrayTick[65536];
ushort curBook,curTick;
ulong  DelaySum=0,DelayCount=0,CountOnBook=0,CountOnTick=0;
int delay,delayMax=0;
CLogger* Logger,*Logger2;
CArrayObj ArrayObj;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   curBook=0;
   curTick=0; 
   DelaySum=0;DelayCount=0;CountOnBook=0;CountOnTick=0;delayMax=0;
   ArrayInitialize(TimeArrayBook,INT_MAX);
   ArrayInitialize(TimeArrayTick,INT_MAX);
   Logger=CLogger::GetLogger();
   Logger2= new CLogger();
   Logger.SetSetting(__FILE__+"\\",Symbol()+"_"+EnumToString(Period())+"_"+TimeToString(TimeCurrent(),TIME_DATE));  
   Logger2.SetSetting(__FILE__+"\\","Alt_"+Symbol()+"_"+EnumToString(Period())+"_"+TimeToString(TimeCurrent(),TIME_DATE));  
  if(Mode == USE_BOOK) is_book = MarketBookAdd(Symbol());
  ArrayObj.Shutdown();
  if (EventSetTimer(SecForPrint) &&  Logger.Init() && Logger2.Init()) 
  return(INIT_SUCCEEDED);
  else return (INIT_FAILED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
  if(Mode == USE_BOOK)
  {
    if(is_book == true) MarketBookRelease(Symbol());
  }  
   delete Logger;
   delete Logger2;
}
//+------------------------------------------------------------------+
//| BookEvent function                                               |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
{  
  TimeArrayBook[curBook++]=GetMicrosecondCount();
  CountOnBook++;
  //Print(__FUNCTION__, " ",curBook);
}
void OnTick()
{
  TimeArrayTick[curTick++]=GetMicrosecondCount();
  CountOnTick++;
  //Print(__FUNCTION__, " ",curTick);

}
//+------------------------------------------------------------------+
void OnTimer()
  {
   string out=NULL;
   int total=MathMax(curBook,curTick);
   int i=0,k=0;
   while(i<total)
     {
      while(i<total && TimeArrayBook[i]<TimeArrayTick[k] )
        {
          MyPrint("Book "+TimeArrayBook[i++]);
        }    
      if(k<curTick-1)
        {
        if(i<total)
          {
           delay=TimeArrayBook[i]-TimeArrayTick[k];
           if (delay>delayMax) 
            delayMax=delay;
           if (delay>0)
              {
                 DelaySum+=delay;
                 DelayCount++;
              }
          }
         MyPrint("Tick "+TimeArrayTick[k++]+ " delay mcs "+delay);
        }       
        i++;
     }
     if (curTick>0)
     {
     MyPrint("Tick "+TimeArrayTick[curTick-1]+ " last");
     string out="Count Event Book after Tick "+DelayCount+". Delay Average "+DoubleToString(DelaySum/DelayCount,2)+". Max "+delayMax+" OnBooks "+CountOnBook+" OnTicks "+CountOnTick;
     MyPrint (out);
     Comment(out);
     }
     Logger.Logger();
     Alt();
     curBook=0;
     curTick=0;
  }
//---
void MyPrint(string out)  
{
   Print(out);
   Logger.Log(__FUNCTION__,out,2,false);
}
//---
void Alt()
{
int last=ArrayObj.Total();
for(int i=0;i<curBook;i++)
  {
   if (!ArrayObj.Add(new CMcsOn(TimeArrayBook[i],USE_BOOK)))
      Logger2.Log(__FUNCTION__,"Error Book Add",0);   
  }
for(int i=0;i<curTick;i++)
  {
   if (!ArrayObj.Add(new CMcsOn(TimeArrayTick[i],USE_TICK)))
      Logger2.Log(__FUNCTION__,"Error Tick Add",0);   
  }
  ArrayObj.Sort();
  int total=ArrayObj.Total();
  total-=last;
  CMcsOn*Obj;
  for(int i=0;i<total;i++)
    {    
     Obj=ArrayObj.At(i);
     if(CheckPointer(Obj)==POINTER_INVALID )
      { Logger2.Log(__FUNCTION__,"Error At Array",0); continue;}
      string out = Obj.USE_BOOK ? "Book ": "Tick ";
      out+= Obj.mcs  ;
      Logger2.Log(__FUNCTION__,out,2);
    }
   Logger2.Log("ArrayObj_","Last "+last+" total "+ArrayObj.Total(),1);  
   Logger2.Logger();
   //ArrayObj.Shutdown(); 
}
 
prostotrader:
А вот интересно, топикстартер удовлетворен ответами на свой вопрос?

Все ответы я уже получил и выводы для себя сделал.
Мне нужно анализировать ленту сделок за фиксированный период времени - цены сделок, реализованные объемы и т.д.
А также нужно моделировать работу алгоритма в тестере стратегий.
Событие OnTick прекрасно с этим справляется, результаты реальной торговли и результаты моделирования в тестере сходятся с небольшой погрешностью, которая меня удовлетворяет.
Если нужно более быстрый анализ ленты сделок, то можно применять OnTimer.

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

 
Vladimir Mikhailov:


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

Как раз наоборот, каждый тик(событие) пришедший в обработчик ОнТик, должен быть синхронен с ОнБук.
Смотри, в обработчике ОнТик есть три события, изменение цены лучшего бид, изменение цены лучшего аск, и трейд(last).
Если измениться цена бид или цена аск без трейда, это будет событие, и в ОнТик придут эти события.
И ОнБук так же эти события должен отловить, только свои события, своего обработчика, иначе будут рассогласования цен бид аск между обработчиками.

А если в ОнТик приходит событие last, значит прошёл трейд.
Трейд порождает событие в ОнБук, ведь после трейда цена или объем банда, изменяться в стакане.
Получается замкнутый круг.

Как в ОнТик так и в ОнБук, есть события лучший бид и лучший аск.
Эти события должны быть всегда синхронны в обоих обработчиках.
А событие last само по себе, оно порождает событие в ОнБук после трейда.
По этому любое событие пришедшее в обработчик ОнТик, должно синхронно отражаться в ОнБук.