リアルタイムで見るTiki - ページ 15

 
Yuriy Zaytsev:

しかし、ログを見ると、同じティックが4秒の差で表示されているのです。

追伸

私は「ありえない」という言葉が嫌いで、「何が起きてもおかしくない」という考え方に慣れてしまっているんです。

ところで、話はそれますが、かつて地球が丸いという主張に対して、「そんなはずはない」というようなことも言っていました。

一般的に私は、自分が確認してからダブルチェックし、できれば他の人が何度かダブルチェックするまで、常に疑念を抱いています。


ログを生成するコードとデータを処理するコードを間違えてませんか? 4秒の差ですからね。

チックはすでに端末にある、つまりネットワーク経由で送信されたものです。

コードをパブリックドメインにする

if(SymbolInfoTick(Symbol(), s_tick) == true)
  {
    Print("SymbolInfoTick: ",GetTickDescription(s_tick));
  }

そして、自分の目で確かめてください。

 
prostotrader:

チックはすでに端末内にある、つまりネットワーク経由で送信されている。

オープンアクセスコードを入れる

そして、自分の目で確かめてください。

ありがとうございます。試してみます。ずっとこの話題を追っていたのですが、研究者としての興味の方が強いです。

このコードは4秒間ラグがあったのですか?

//+------------------------------------------------------------------+
//|                                                   Ticks_test.mq5 |
//|                                      Copyright 2019 prostotrader |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019 prostotrader"
#property link      "https://www.mql5.com"
#property version   "1.00"
//---
bool is_book;
MqlTick ticks[], s_tick;
ulong last_time, mem_cnt, tot_cnt;
bool is_first;
int t_cnt, result;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
  tot_cnt = 0;
  is_book = MarketBookAdd(Symbol());
  result = CopyTicks(Symbol(), ticks, COPY_TICKS_ALL, 0, 1);
  if(result > 0)
  {
    last_time = ulong(ticks[0].time_msc); //запоминаем время последнего известного тика
    is_first = true;
  }
  else
  {
    is_first = false;
    Alert("No start time!");
    return(INIT_FAILED);
  } 
  ArraySetAsSeries(ticks, true);  
  return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+ 
//| возвращает строковое описание тика                               | 
//+------------------------------------------------------------------+ 
string GetTickDescription(MqlTick &tick) 
  { 
   string res = string(tick.time) + "." +  string(tick.time_msc%1000); 
// 
   bool buy_tick = ((tick.flags&TICK_FLAG_BUY)==TICK_FLAG_BUY); 
   bool sell_tick = ((tick.flags&TICK_FLAG_SELL)==TICK_FLAG_SELL); 
   bool ask_tick = ((tick.flags&TICK_FLAG_ASK)==TICK_FLAG_ASK); 
   bool bid_tick = ((tick.flags&TICK_FLAG_BID)==TICK_FLAG_BID); 
   bool last_tick = ((tick.flags&TICK_FLAG_LAST)==TICK_FLAG_LAST); 
   bool volume_tick = ((tick.flags&TICK_FLAG_VOLUME)==TICK_FLAG_VOLUME); 
// 
   if((buy_tick== true) || (sell_tick == true)) 
   { 
     res = res + (buy_tick?StringFormat(" Buy Tick: Last=%G Volume=%d ",tick.last,tick.volume):""); 
     res = res + (sell_tick?StringFormat(" Sell Tick: Last=%G Volume=%d ",tick.last,tick.volume):""); 
     res = res + (ask_tick?StringFormat(" Ask=%G ",tick.ask):""); 
     res = res + (bid_tick?StringFormat(" Bid=%G ",tick.bid):""); 
   } 
   else 
   { 
     res = res + (ask_tick?StringFormat(" Ask=%G ",tick.ask):""); 
     res = res + (bid_tick?StringFormat(" Bid=%G ",tick.bid):""); 
     res = res + (last_tick?StringFormat(" Last=%G ",tick.last):""); 
     res = res + (volume_tick?StringFormat(" Volume=%d ",tick.volume):""); 
   } 
   return res; 
  } 
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
  if(is_book == true) MarketBookRelease(Symbol());
}
//+------------------------------------------------------------------+
//| BookEvent function                                               |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
{
  if(symbol != Symbol()) return;
  tot_cnt++;
  if(SymbolInfoTick(Symbol(), s_tick) == true)
  {
    Print("SymbolInfoTick: ",GetTickDescription(s_tick));
  }
  if(is_first == true)
  {
    result = CopyTicks(Symbol(), ticks, COPY_TICKS_ALL, last_time, 0); //копируем все вновь пришедшие тики от последнего известного времени
    if(result > 0)
    {
      t_cnt = 0;
      for(int i= 0; i<result; i++)
      {
        if(ticks[i].time_msc == ticks[0].time_msc) t_cnt++;             //Считаем кол-во тиков с одинаковым временем
        Print(__FUNCTION__, ": ",GetTickDescription(ticks[i]));
      }
    //  l_tick = ticks[0];
      is_first = false;
      last_time = ulong(ticks[0].time_msc);                             //Запоминаем время последнего тика
    } 
  }
  else
  {
    if(SymbolInfoTick(Symbol(), s_tick) == true)
    {
      Print("SymbolInfoTick: ",GetTickDescription(s_tick));
    }
    result = CopyTicks(Symbol(), ticks, COPY_TICKS_ALL, last_time, 0); //забираем тики из последнего (посчитанного пакета тикив и считываем тики из нового пакета)
    if(result > 0)
    {
     // l_tick = ticks[0];
      if(result > t_cnt)
      {
        mem_cnt = t_cnt;
        t_cnt = 0;
        for(int i= 0; i<(result - int(mem_cnt)); i++)
        {
          if(ticks[i].time_msc == ticks[0].time_msc) t_cnt++;           //Считаем кол-во тиков с одинаковым временем
          Print(__FUNCTION__, ": ",GetTickDescription(ticks[i]));
        } 
        if(last_time == ulong(ticks[0].time_msc))
        {
          t_cnt += int(mem_cnt);
        }
        else last_time = ulong(ticks[0].time_msc);
      }
      else
      {
        Print(__FUNCTION__, ": Pending order!");                          //Изменения стакана (добавлен/удален отложенный ордер)
      }
    }
    else
    {
      Print(__FUNCTION__, ": Pending order!");                          //Изменения стакана (добавлен/удален отложенный ордер)
    }
  }
}
//+------------------------------------------------------------------+
 

これではないようです。

コードにOnTickが見当たらない。

 

どうやらこれが問題のコードだったようです

//+------------------------------------------------------------------+
//|                                                   Ticks_test.mq5 |
//|                                      Copyright 2019 prostotrader |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019 prostotrader"
#property link      "https://www.mql5.com"
#property version   "1.00"
//---
bool is_book;
MqlTick ticks[];
ulong last_time, mem_cnt, tot_cnt;
bool is_first;
int t_cnt, result;
enum ENUM_BOOK_OR_TICK
{
        USE_BOOK,       // Use OnBookEvent
        USE_TICK        // Use OnTick
};

input ENUM_BOOK_OR_TICK Mode = USE_BOOK;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
  tot_cnt = 0;
  if(Mode == USE_BOOK) is_book = MarketBookAdd(Symbol());
  result = CopyTicks(Symbol(), ticks, COPY_TICKS_ALL, 0, 1);
  if(result > 0)
  {
    last_time = ulong(ticks[0].time_msc); //запоминаем время последнего известного тика
    is_first = true;
  }
  else
  {
    is_first = false;
    Alert("No start time!");
    return(INIT_FAILED);
  } 
  ArraySetAsSeries(ticks, true);  
  return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+ 
//| возвращает строковое описание тика                               | 
//+------------------------------------------------------------------+ 
string GetTickDescription(MqlTick &tick) 
  { 
   string res = string(tick.time) + "." +  string(tick.time_msc%1000); 
// 
   bool buy_tick = ((tick.flags&TICK_FLAG_BUY)==TICK_FLAG_BUY); 
   bool sell_tick = ((tick.flags&TICK_FLAG_SELL)==TICK_FLAG_SELL); 
   bool ask_tick = ((tick.flags&TICK_FLAG_ASK)==TICK_FLAG_ASK); 
   bool bid_tick = ((tick.flags&TICK_FLAG_BID)==TICK_FLAG_BID); 
   bool last_tick = ((tick.flags&TICK_FLAG_LAST)==TICK_FLAG_LAST); 
   bool volume_tick = ((tick.flags&TICK_FLAG_VOLUME)==TICK_FLAG_VOLUME); 
// 
   if((buy_tick== true) || (sell_tick == true)) 
   { 
     res = res + (buy_tick?StringFormat(" Buy Tick: Last=%G Volume=%d ",tick.last,tick.volume):""); 
     res = res + (sell_tick?StringFormat(" Sell Tick: Last=%G Volume=%d ",tick.last,tick.volume):""); 
     res = res + (ask_tick?StringFormat(" Ask=%G ",tick.ask):""); 
     res = res + (bid_tick?StringFormat(" Bid=%G ",tick.bid):""); 
   } 
   else 
   { 
     res = res + (ask_tick?StringFormat(" Ask=%G ",tick.ask):""); 
     res = res + (bid_tick?StringFormat(" Bid=%G ",tick.bid):""); 
     res = res + (last_tick?StringFormat(" Last=%G ",tick.last):""); 
     res = res + (volume_tick?StringFormat(" Volume=%d ",tick.volume):""); 
   } 
   return res; 
  } 
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
  if(Mode == USE_BOOK)
  {
    Print("USE_BOOK ticks received: ", tot_cnt);
    if(is_book == true) MarketBookRelease(Symbol());
  }
  else
  {
    Print("USE_TICK ticks received: ", tot_cnt);
  }  
}
//+------------------------------------------------------------------+
//| BookEvent function                                               |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
{
  if ( Mode != USE_BOOK || symbol != Symbol() ) return;
  tot_cnt++;
  if(is_first == true)
  {
    result = CopyTicks(Symbol(), ticks, COPY_TICKS_ALL, last_time, 0); //копируем все вновь пришедшие тики от последнего известного времени
    if(result > 0)
    {
      t_cnt = 0;
      for(int i= 0; i<result; i++)
      {
        if(ticks[i].time_msc == ticks[0].time_msc) t_cnt++;             //Считаем кол-во тиков с одинаковым временем
        Print(__FUNCTION__, ": ",GetTickDescription(ticks[i]));
      }
      is_first = false;
      last_time = ulong(ticks[0].time_msc);                             //Запоминаем время последнего тика
    } 
  }
  else
  {
    result = CopyTicks(Symbol(), ticks, COPY_TICKS_ALL, last_time, 0); //забираем тики из последнего (посчитанного пакета тикив и считываем тики из нового пакета)
    if(result > 0)
    {
      if(result > t_cnt)
      {
        mem_cnt = t_cnt;
        t_cnt = 0;
        for(int i= 0; i<(result - int(mem_cnt)); i++)
        {
          if(ticks[i].time_msc == ticks[0].time_msc) t_cnt++;           //Считаем кол-во тиков с одинаковым временем
          Print(__FUNCTION__, ": ",GetTickDescription(ticks[i]));
        } 
        if(last_time == ulong(ticks[0].time_msc))
        {
          t_cnt += int(mem_cnt);
        }
        else last_time = ulong(ticks[0].time_msc);
      }
      else
      {
        Print(__FUNCTION__, ": Pending order!");                          //Изменения стакана (добавлен/удален отложенный ордер)
      }
    }
    else
    {
      Print(__FUNCTION__, ": Pending order!");                            //Изменения стакана (добавлен/удален отложенный ордер)
    }
  }
}
//+------------------------------------------------------------------+
//| OnTick function                                                  |
//+------------------------------------------------------------------+
void OnTick()
{
  if ( Mode != USE_TICK ) return;
  tot_cnt++;
  if(is_first == true)
  {
    result = CopyTicks(Symbol(), ticks, COPY_TICKS_ALL, last_time, 0); //копируем все вновь пришедшие тики от последнего известного времени
    if(result > 0)
    {
      t_cnt = 0;
      for(int i= 0; i<result; i++)
      {
        if(ticks[i].time_msc == ticks[0].time_msc) t_cnt++;             //Считаем кол-во тиков с одинаковым временем
        Print(__FUNCTION__, ": ",GetTickDescription(ticks[i]));
      }
      is_first = false;
      last_time = ulong(ticks[0].time_msc);                             //Запоминаем время последнего тика
    } 
  }
  else
  {
    result = CopyTicks(Symbol(), ticks, COPY_TICKS_ALL, last_time, 0); //забираем тики из последнего (посчитанного пакета тикив и считываем тики из нового пакета)
    if(result > 0)
    {
      if(result > t_cnt)
      {
        mem_cnt = t_cnt;
        t_cnt = 0;
        for(int i= 0; i<(result - int(mem_cnt)); i++)
        {
          if(ticks[i].time_msc == ticks[0].time_msc) t_cnt++;           //Считаем кол-во тиков с одинаковым временем
          Print(__FUNCTION__, ": ",GetTickDescription(ticks[i]));
        } 
        if(last_time == ulong(ticks[0].time_msc))
        {
          t_cnt += int(mem_cnt);
        }
        else last_time = ulong(ticks[0].time_msc);
      }
      else
      {
        Print(__FUNCTION__, ": Pending order!");                          //Изменения стакана (добавлен/удален отложенный ордер)
      }
    }
    else
    {
      Print(__FUNCTION__, ": Pending order!");                          //Изменения стакана (добавлен/удален отложенный ордер)
    }
  }
}
//+------------------------------------------------------------------+
 

コードに私の時間を追加しました。

OnTick()が発動された時の記憶(t_time = GetMicrosecondCount();)

そして、各機能を実行する際に時間をかけます。

   t_time = GetMicrosecondCount();
//  if(symbol != Symbol()) return;
  //tot_cnt++;
  if(SymbolInfoTick(Symbol(), s_tick) == true)
  {
    func_time = GetMicrosecondCount();
    Print("SymbolInfoTick: time = ", string(func_time - t_time), " mcs ", GetTickDescription(s_tick));
  }
  double a_ask, a_bid, a_last;
  if (SymbolInfoDouble(Symbol(), SYMBOL_ASK, a_ask) == true)
  {
    func_time = GetMicrosecondCount();
    Print("SymbolInfoDouble: time = ", string(func_time - t_time), " mcs ", "ask = ", a_ask);
  }
  if (SymbolInfoDouble(Symbol(), SYMBOL_BID, a_bid) == true)
  {
    func_time = GetMicrosecondCount();
    Print("SymbolInfoDouble: time = ", string(func_time - t_time), " mcs ", "bid = ", a_bid);
  }
  if (SymbolInfoDouble(Symbol(), SYMBOL_LAST, a_last) == true)
  {
    func_time = GetMicrosecondCount();
    Print("SymbolInfoDouble: time = ", string(func_time - t_time), " mcs ", "last = ", a_last);
  }
  if(is_first == true)
  {
    result = CopyTicks(Symbol(), ticks, COPY_TICKS_ALL, last_time, 0); //копируем все вновь пришедшие тики от последнего известного времени
    if(result > 0)
    {
      func_time = GetMicrosecondCount();
      t_cnt = 0;
      for(int i= 0; i<result; i++)
      {
        if(ticks[i].time_msc == ticks[0].time_msc) t_cnt++;             //Считаем кол-во тиков с одинаковым временем
        Print(__FUNCTION__, ": time = ", string(func_time - t_time), " mcs ", GetTickDescription(ticks[i]));

その結果

2020.02.04 13:09:13.101 Ticks_test (GOLD-3.20,M1)       SymbolInfoTick: time = 2 mcs 2020.02.04 13:09:10.720 Bid=1573.1 
2020.02.04 13:09:13.101 Ticks_test (GOLD-3.20,M1)       SymbolInfoDouble: time = 28 mcs ask = 1573.3
2020.02.04 13:09:13.101 Ticks_test (GOLD-3.20,M1)       SymbolInfoDouble: time = 33 mcs bid = 1573.1
2020.02.04 13:09:13.101 Ticks_test (GOLD-3.20,M1)       SymbolInfoDouble: time = 36 mcs last = 1573.4
2020.02.04 13:09:13.101 Ticks_test (GOLD-3.20,M1)       OnTick: time = 41 mcs 2020.02.04 13:09:10.720 Bid=1573.1 
2020.02.04 13:09:13.101 Ticks_test (GOLD-3.20,M1)       OnTick: time = 41 mcs 2020.02.04 13:09:00.328 Ask=1573.3 

つまり、各機能の実行時間は50マイクロ秒 以下です。

4秒はどこからやってくるのか?

1つのターミナルで2つのEAが動いていて、単にターミナルに時間がないだけだと思います。

すべての情報を1つのログに「統合」する時間がないため、必要なローカル タイムを設定するのです。

 
prostotrader:

トレーディングでは、個人的には非同期 注文を使っています。

取引所で真剣に取引するならば)株式市場のすべての 変化が必要だということです。

そして、このイベントが早く来れば来るほど良いのです。

私自身は、OnBookに代わるものはないと考えて います。

原則として、OnBookからトレードオペレーションを 直接呼び出すことは可能です。OnBookで必要なのは、操作を実行するためのフラグを形成し、フラグ自体を別の場所で処理することです。つまり、操作自体はイベントを作成する形成されたフラグによって別の手順で開始する必要がありますが、手順HeBookを去った後、コードOnBook自体は、重い操作から解放されます。しかし、注文が非同期で開かれ、条件の処理が非常識に大きくなることがなければ、大きな遅延が発生することはないと思われます。

 
prostotrader:

コードに私の時間を追加しました。

OnTick()が発動された時の記憶(t_time = GetMicrosecondCount();)

そして、各機能を実行する際に時間をかけます。

その結果

つまり、各機能の実行時間は50マイクロ秒 以下です。

4秒はどこからやってくるのか?

1つのターミナルで2つのEAが動いていて、単にターミナルに時間がないだけだと思います。

ターミナルには、すべての情報を1つのログファイルに「ダンプ」する時間がないため、必要だと思われるローカル タイムを設定するのです。

たしかに、そんな野暮なラグは現実的ではないのでしょう。


1 「FLUSHは、MQが自分で決めた時に効いている!

2 - ハードディスクの集中作業によるディスクへの書き込みの技術的な遅延。


ローカルマシンにすでに書き込みキューがある可能性があります - これはかなり現実的で、私は数テラバイトのバックアップがディスクに注がれた経験があります。

としか思えません。

例えば、Microsoft Officeを実行し、Windowsをアップデートし、インターネットから映画を録画した場合、あるいはローカルマシンでMS SQLを同時に扱う場合など、数テラバイトのバックアップがディスクに取り込まれたことがあります。

バックアップをいくつか取り、4つのトレントを12個、そして2、3個のプログラムをディスクに激しく書き込んでいます。

つまり、ディスクで集中的に作業した場合、ログをディスクに書き込むのが遅れた可能性があるということです。

 
Yuriy Zaytsev:

おそらくそうだろう、これだけ乱暴な遅延では現実的でない。


1 - MQが勝手に決めたことなのにFLUSHが効いた!

2 - ハードディスクへの集中的な作業により発生する、ディスクへの書き込みの技術的な遅延。


ローカルマシンがすでに書き込みキューに入っている可能性もあり、実現可能です。私は数テラバイトのバックアップデータをディスク上に流出させた経験があります

としか思えません。

例えば、マイクロソフトオフィスのインストール、システムのアップデート、インターネットからのムービーの上書きなどを同時に行う必要があったり、ローカルマシンでMS SQLを使った作業を行うなど、数テラバイトのバックアップをディスクに流し込んでいることがあります。

バックアップをいくつか取り、4つのトレントを12個、そしてディスクに集中的に書き込むプログラムを2、30個持っています。

つまり、ディスクで集中的に作業していたのなら、ディスクへのロギングに遅れが生じた可能性は十分にあるのです。

ディスクにはほとんど関係なく、MTはログをキャッシュに書き込むときにすでに時間を置いています。4秒間の端末全般は、一般的なシステム負荷、もっと言えばRAMやCPUが関係しているのかもしれないと思ったのはそういうことです。
 
prostotrader:

コードに私の時間を追加しました。

OnTick()が発動された時の記憶(t_time = GetMicrosecondCount();)

そして、各機能を実行する際に時間をかけます。

その結果

つまり、各機能の実行時間は50マイクロ秒 以下です。

4秒はどこからやってくるのか?

1つのターミナルで2つのEAが動いていて、単にターミナルに時間がないだけだと思います。

「そのため、必要と思われるローカル タイムを設定します。

ところで - ロギング時間に捕らわれないように - 以下のコードで形成する配列にローカルタイムを追加することができます。

そうすると、端末がログをディスクにリセットした時刻と、OnBookのティックやイベントがローカルに 到着した時刻とで、ログに明らかな違いが出てきます。

そして、この方が研究面でも正しいでしょう。

//+------------------------------------------------------------------+ 
//| возвращает строковое описание тика                               | 
//+------------------------------------------------------------------+ 
string GetTickDescription(MqlTick &tick) 
  { 
   string res = string(tick.time) + "." +  string(tick.time_msc%1000); 
// 
   bool buy_tick = ((tick.flags&TICK_FLAG_BUY)==TICK_FLAG_BUY); 
   bool sell_tick = ((tick.flags&TICK_FLAG_SELL)==TICK_FLAG_SELL); 
   bool ask_tick = ((tick.flags&TICK_FLAG_ASK)==TICK_FLAG_ASK); 
   bool bid_tick = ((tick.flags&TICK_FLAG_BID)==TICK_FLAG_BID); 
   bool last_tick = ((tick.flags&TICK_FLAG_LAST)==TICK_FLAG_LAST); 
   bool volume_tick = ((tick.flags&TICK_FLAG_VOLUME)==TICK_FLAG_VOLUME); 
// 
   if((buy_tick== true) || (sell_tick == true)) 
   { 
     res = res + (buy_tick?StringFormat(" Buy Tick: Last=%G Volume=%d ",tick.last,tick.volume):""); 
     res = res + (sell_tick?StringFormat(" Sell Tick: Last=%G Volume=%d ",tick.last,tick.volume):""); 
     res = res + (ask_tick?StringFormat(" Ask=%G ",tick.ask):""); 
     res = res + (bid_tick?StringFormat(" Bid=%G ",tick.bid):""); 
   } 
   else 
   { 
     res = res + (ask_tick?StringFormat(" Ask=%G ",tick.ask):""); 
     res = res + (bid_tick?StringFormat(" Bid=%G ",tick.bid):""); 
     res = res + (last_tick?StringFormat(" Last=%G ",tick.last):""); 
     res = res + (volume_tick?StringFormat(" Volume=%d ",tick.volume):""); 
   } 
   return res; 
  } 
 
Aleksey Mavrin:
ディスクとはほとんど関係なく、 MTはログをキャッシュに書き込むときにすでに時刻を設定 する。以上、4秒間の端末全般は、RAMやCPUではなく、システム全体の負荷が関係しているのかもしれないと思いました。

というのは確かなのでしょうか?


4秒って、まさか! プロセッサが4秒間フリーズしたとか、メモリが4秒間解放されたとか、本気で思ってるのか?

ディスクの書き込みキューが原因である可能性が高いです。

ディスクはメモリーやプロセッサーより遅い。

flush()コマンドはC言語のコマンドで、ご存知の方も多いと思いますが、気持ちの良い時に実行され、ディスクブートの関係で遅れることもあります。

バッファをディスクにダンプすることをそう呼ぶのです。