ライブラリ: オンティック・マルチ - ページ 5

 
Stanislav Korotky #:

本格的なテストケースを作成するには、具体的な課題を理解する必要があります。つまり、ミリ秒単位の精度で時間が一致したティックのみを取引するということですか?

いいえ。課題は、すべての銘柄について常に最新の環境を維持することです。私の例では、OnTimer内で常に最新性の条件が満たされています。ポジションの開設はあくまでデモンストレーションです。
 
fxsaber #:
いいえ。重要なのは、すべてのシンボルについて常に最新の環境を維持することです。私の例では、OnTimer内では常に最新性の条件が満たされています。ポジションの開設はあくまで実演のためのものです。

わかりました。

現時点では、OnTickMultiイベントでの実装のどこでティックが「失われる」のか、言い換えれば、 なぜSymbolInfoTickが期待した価格とは異なる値を返すことになるのか(私は以前、インジケーターからのカスタムイベントのディスパッチによる遅延があるのではないかと推測していました) ——実験の純粋さを保つため、スパイインジケーターを使わずにテストしてみたいと思います。通常のOnTickから、少なくとも同じタイムスタンプに対して、すべてのシンボルに対してSymbolInfoTick/CopyTicksを実行するだけです。

OnTimerに関しては、私としては疑問があります(もし私の考えが間違っているなら、ご指摘ください):

void OnTimer()
{
  if (TimeMsc++ == 1759280400081) // 2025年10月1日 01:00:00.081
    PositionOpen();
}

ハンドラが1ミリ秒以内に実行される保証はないため、単にカウンタをインクリメントするだけでは、当初の同期が維持されるとは限りません。つまり、 サーバー時間との照合は、本来なら毎回(ポジションの開設後、あるいはインクリメントよりも複雑な計算が行われた後など)リアルタイムで行う必要があります。 言い換えれば、多くのポジションを開く必要がある場合、この洗練されたアプローチは通用しません。

さらに、初期の同期(カウンタの初期化)も、個人的には100%完璧とは言えません。

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

仮にテスター上では確かにサーバー時間がミリ秒なしで開始されるとしても、そのようなコードがオンライン環境でどのように動作するのでしょうか?そして、なぜ1ミリ秒を加算するのでしょうか?私はやはり、ティックからサーバー時間を取得する方がよいと思います。

これは細かいことを言っているわけではなく、単に「有効性の条件が常に満たされる」という点に対する疑問です。

 
Stanislav Korotky #:

カウンタの単純なインクリメントだけでは、当初の同期が維持されるとは限らない

テスター上では動作が保証されます。

しかし、このようなコードはオンライン環境でどのように動作するのでしょうか?

オンライン環境では、このような問題はありません。なぜなら、端末に届くすべてのデータは参考値であり、ラグによって最新ではないからです。

そして、なぜ1ミリ秒を加算するのでしょうか?

それは、最初のOnTimerが即座ではなく、指定された間隔を経て呼び出されるためです。

 
Stanislav Korotky #:

OnTickMulti イベントでの実装がどこでティックを「失っている」のか、言い換えれば、なぜ SymbolInfoTick が期待された価格とは異なる値を返すことになるのか、現時点では不明です

OnTickMultiは間違いなく原因ではありません。というのも、標準のSymbolInfoTickは、あるシンボルでは正しいティックを返すものの、他のシンボルでは正しく返さないからです。


原因は、ティックの配信順序にあるだけです。

トレーディング、自動売買システム、および取引戦略のテストに関するフォーラム

ライブラリ:OnTickMulti

fxsaber, 2025年9月30日 09:24

同じ時刻のティックは同時に届きません。すべて順次届きます。したがって、EURUSDの時刻がより遅いティックが先に届いた場合、その時点では、2番目に届くはずのGBPUSDの同じ時刻のティックについては何も分かっていません。 したがって、EURUSDの最初のティックが到着した時点では、GBPUSDの2番目のティックは存在せず、GBPUSDの前のティックのデータのみが存在することになります。
 
fxsaber #:

なぜなら、最初のOnTimerはすぐにではなく、指定された間隔を経て呼び出されるからです。

すべてのデータを迅速に更新する方法を考案しました。

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

#include <MT4Orders.mqh> //https://www.mql5.com/ja/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月1日 01:00:00.081
    PositionOpen();
    
  EventKillTimer();
}

この仕組みにより、通常モード(OnTickMultiを使用しない単一通貨モード)であっても、常に最新のデータのみを扱うことが可能になります。例えば、EURUSDには同じ時刻のティックが複数存在する場合があります。更新を行うことで、そのシーケンスの中で最新のティック、つまり最新のデータのみを扱うことができます。


追記:これはカスタムシンボルを作成する もう一つの理由でもあります。つまり、時刻が同じティックシーケンスの中から、最後のティックだけを履歴に記録するのです。そうすれば、単一通貨モードでも常に最新化が維持されます。

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

でも、始値はすべて同じなのに、すべての銘柄の始値の時間は同じですよね。

そして、このような挙動のせいで、一部のシステムを正常にテストすることができません。

始値ベースのテスターモードについて、改めて思い出しました。重要な点は、その名称にもかかわらず、このモードではテスターがOHLCのティックを4つ生成し、名称から直感的に予想されるような1つではないということです。 これらの4つのチェックポイントのうち、エキスパートアドバイザーについては最初のOのみが使用されOnTickが呼び出されますが、インジケーターについては、HLCまたはLHC(バーの方向に応じて)に対して、対応する価格のティックデータを用いてOnCalculateが3回呼び出されます。 これら3つの追加ポイントの時刻は、バーの最後の3秒と人為的に設定されています。つまり、スパイインジケーターはシンボルに対して1つのイベントではなく、複数のイベントを送信することになります。おそらく、始値モードを使用する人はこの点を考慮する必要があるでしょう。

また、(書籍にある同様のスパイインジケーターにデバッグ機能を追加したところ)、前のバーの追加シンボルのティックに関するイベントが、何らかの理由で新しいバーでも繰り返され、 その後になって初めてスパイの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;
}

また、受信時(ここでは1つの追加銘柄の例を示していますが、複数の場合は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 #:
最も簡単な同期方法
時系列データの同期は十分に研究された問題であり、既成のアルゴリズムがあるだろうと考えて、インターネットで検索してみたが、何も見つからなかった