【MQL5】OnBookEvent関数について

MQL5リファレンス
スポンサーリンク
スポンサーリンク
  1. OnBookEvent関数の働き・役割
  2. OnBookEvent関数の引数について
    1. 引数の詳細
  3. OnBookEvent関数の戻り値について
  4. OnBookEvent関数を使う際の注意点
  5. OnBookEvent関数を使ったサンプルコード
  6. 解説1: グローバル領域での定義
    1. 各変数の解説
      1. 入力パラメータ
      2. BookEventイベント購読のフラグ
      3. 板情報からリクエストを受け取る配列
    2. MqlBookInfo構造体
  7. 解説2: OnInit関数
    1. 各部分の解説
      1. 最初のティックを待つメッセージをコメントに表示
      2. 板情報ブロードキャストを有効にする
      3. 初期化が正常に完了
    2. フォーマット指定子について
  8. 解説3: OnDeinit関数
    1. 各部分の解説
      1. 初期化解除の理由コードを表示する
      2. 板情報イベント受信の購読を取り消す
  9. 解説4: OnBookEvent関数その1
    1. 各部分の解説
      1. テスト開始時刻を保持する変数
      2. 板情報更新カウンタ
      3. 板情報イベントを扱うのは、自分たちで購読している場合のみ
      4. 特定シンボルのみの更新を数える
  10. 解説5: OnBookEvent関数その2
    1. 各部分の解説
      1. 最初のティックをスキップしてカウンタをクリアする
      2. 最初のティックを受け取った時に開始時刻を記憶する
      3. 板情報データをリクエストする
      4. テスト終了時の時間を計算
  11. 解説6: OnBookEvent関数その3
    1. 各部分の解説
      1. テストの開始からどれくらいの時間が経過したかを確認
      2. テストの結果をログに出力
      3. エキスパートを削除
      4. カウンタをコメント欄に表示する

OnBookEvent関数の働き・役割

OnBookEvent関数は、BookEventイベント(新しい注文の追加や既存の注文のキャンセルなど)が発生したときにインジケータEAで呼び出される関数です。
この関数を使用することで、特定の銘柄に対する板情報の変化を検出し、それに対して適切な処理を行うことができます。

板情報とは、特定の銘柄に対する売り注文や買い注文の詳細情報を指します。具体的には、どの価格でどれだけの注文が入っているかを示す情報です。これにより、投資家は市場の動向をリアルタイムで把握し、売買の判断を行うことができます。

OnBookEvent関数は、MarketBookAdd関数を使用して特定の銘柄に対するBookEventイベントの購読を設定した場合にのみ発生します。
購読を設定することにより、指定した銘柄に対するBookEventイベントを受信し、その変化を処理することが可能になります。

OnBookEvent関数の引数について

OnBookEvent関数は、以下のような引数を持ちます。

引数の詳細

  • symbol(銘柄)

symbolはBookEventが到着した銘柄の名称を示します。この引数により、どの銘柄に対するイベントが発生したのかを特定することができます。例えば、”EURUSD”や”USDJPY”といった具体的な銘柄名が渡されます。

以下に、OnBookEvent関数の定義を示します。

void OnBookEvent(
   const string &symbol  // 銘柄
);

OnBookEvent関数の戻り値について

OnBookEvent関数は、戻り値を持ちません。この関数void型で定義されており、銘柄の板情報に対する処理を行うためのイベントハンドラとして機能します。イベント発生時に適切な処理を実行するための関数です。

OnBookEvent関数を使う際の注意点

特定の銘柄に対するBookEventイベントを受信するためには、MarketBookAdd関数を使用してその銘柄の購読を設定する必要があります。
購読を停止する場合はMarketBookRelease関数を使用します。

BookEventイベントはすべてのチャートにブロードキャスト(全チャートに配信)されるため、1つのEAインジケータMarketBookAdd関数を使用してイベントの購読を設定すると、同じチャート上の他のすべてのOnBookEventハンドラもそのイベントを受け取ります。
そのため、symbolパラメータとして渡された銘柄名を分析して、自分が処理すべき銘柄のイベントかどうかを確認する必要があります。

購読カウンタの初期値はゼロであり、MarketBookAdd関数を呼び出すたびに1つ増加し、MarketBookRelease関数を呼び出すたびに1つ減少します。購読カウンタがゼロになるまで、BookEventイベントはチャート内でブロードキャストされ続けます。
プログラム全体のライフタイム(実行期間)でMarketBookAdd関数MarketBookRelease関数の呼び出し数が偶数になるように注意してください。これは、呼び出し数が偶数でないと、MarketBookRelease関数が正しく呼び出されずに購読が解除されないためです。

また、BookEventイベントはスキップされることなく、前のイベントの処理が完了していなくてもキュー(処理待ちの命令リスト)に入れられるため、イベントが連続して発生する場合は処理が遅延する可能性があります。

OnBookEvent関数を使ったサンプルコード


//--- 入力パラメータ
input ulong ExtCollectTime   =30; // 秒単位でのテスト時間
input ulong ExtSkipFirstTicks=10; // 初めに抜かされるティックの数

//--- BookEventイベント購読のフラグ
bool book_subscribed=false; 

//--- 板情報からリクエストを受け取る配列
MqlBookInfo  book[]; 

//+------------------------------------------------------------------+ 
//| エキスパート初期化関数                                           | 
//+------------------------------------------------------------------+ 
int OnInit() 
{ 
   //--- 最初のティックを待つメッセージをコメントに表示
   Comment(StringFormat("Waiting for the first %I64u ticks to arrive",ExtSkipFirstTicks)); 
   PrintFormat("Waiting for the first %I64u ticks to arrive",ExtSkipFirstTicks); 

   //--- 板情報ブロードキャストを有効にする 
   if(MarketBookAdd(_Symbol)) 
   { 
      book_subscribed=true; // 成功した場合、フラグをtrueに設定
      PrintFormat("%s: MarketBookAdd(%s) function returned true",__FUNCTION__,_Symbol); 
   } 
   else 
   {
      PrintFormat("%s: MarketBookAdd(%s) function returned false! GetLastError()=%d",__FUNCTION__,_Symbol,GetLastError()); 
   }

   //--- 初期化が正常に完了 
   return(INIT_SUCCEEDED); 
} 

//+------------------------------------------------------------------+ 
//| エキスパートの初期化を解除する関数                              | 
//+------------------------------------------------------------------+ 
void OnDeinit(const int reason) 
{ 
   //--- 初期化解除の理由コードを表示する 
   Print(__FUNCTION__,": Deinitialization reason code = ",reason);   

   //--- 板情報イベント受信の購読を取り消す 
   if(book_subscribed) 
   { 
      if(!MarketBookRelease(_Symbol)) 
      {
         PrintFormat("%s: MarketBookRelease(%s) returned false! GetLastError()=%d",__FUNCTION__,_Symbol,GetLastError()); 
      }
      else 
      {
         book_subscribed=false; // 成功した場合、フラグをfalseに設定
      }
   }
} 

//+------------------------------------------------------------------+ 
//| BookEvent関数                                                    | 
//+------------------------------------------------------------------+ 
void OnBookEvent(const string &symbol) 
{ 
   //--- テスト開始時刻を保持する変数
   static ulong starttime=0;        

   //--- 板情報更新カウンタ
   static ulong tickcounter=0;      

   //--- 板情報イベントを扱うのは、自分たちで購読している場合のみ
   if(!book_subscribed) 
      return; 

   //--- 特定シンボルのみの更新を数える
   if(symbol!=_Symbol) 
      return; 

   //--- 最初のティックをスキップしてカウンタをクリアする
   tickcounter++; 
   if(tickcounter<ExtSkipFirstTicks) 
      return; 

   //--- 最初のティックを受け取った時に開始時刻を記憶する
   if(tickcounter==ExtSkipFirstTicks)  
      starttime=GetMicrosecondCount(); 

   //--- 板情報データをリクエストする
   MarketBookGet(symbol,book); 

   //--- テスト終了時の時間を計算
   ulong endtime=GetMicrosecondCount()-starttime; 

   //--- 実際のティック数を計算
   ulong ticks  =1+tickcounter-ExtSkipFirstTicks; 

   //--- テストの開始からどれくらいの時間が経過したかを確認
   if(endtime>ExtCollectTime*1000*1000)  
   { 
      //--- テストの結果をログに出力
      PrintFormat("%I64u ticks for %.1f seconds: %.1f ticks/sec ",ticks,endtime/1000.0/1000.0,ticks*1000.0*1000.0/endtime); 

      //--- エキスパートを削除
      ExpertRemove(); 
      return; 
   } 

   //--- カウンタをコメント欄に表示する
   if(endtime>0) 
      Comment(StringFormat("%I64u ticks for %.1f seconds: %.1f ticks/sec ",ticks,endtime/1000.0/1000.0,ticks*1000.0*1000.0/endtime)); 
}

解説1: グローバル領域での定義

//--- 入力パラメータ
input ulong ExtCollectTime   =30; // 秒単位でのテスト時間
input ulong ExtSkipFirstTicks=10; // 初めに抜かされるティックの数

//--- BookEventイベント購読のフラグ
bool book_subscribed=false; 

//--- 板情報からリクエストを受け取る配列
MqlBookInfo  book[]; 

各変数の解説

入力パラメータ

  • ExtCollectTime
  • このパラメータは、テストの実行時間を秒単位で設定します。初期値は30秒です。
  • ExtSkipFirstTicks
  • このパラメータは、最初に無視するティックの数を設定します。初期値は10ティックです。

BookEventイベント購読のフラグ

  • book_subscribed
  • このフラグは、BookEventイベントの購読が設定されているかどうかを示します。初期値はfalseで、購読が成功した場合にtrueに設定されます。

板情報からリクエストを受け取る配列

MqlBookInfo構造体

解説2: OnInit関数

以下のコードは、エキスパートアドバイザーEA)の初期化を担うOnInit関数の定義です。この関数は、EAが初めて実行されるときに呼び出され、必要な初期設定を行います。

//+------------------------------------------------------------------+ 
//| エキスパート初期化関数                                           | 
//+------------------------------------------------------------------+ 
int OnInit() 
{ 
   //--- 最初のティックを待つメッセージをコメントに表示
   Comment(StringFormat("Waiting for the first %I64u ticks to arrive",ExtSkipFirstTicks)); 
   PrintFormat("Waiting for the first %I64u ticks to arrive",ExtSkipFirstTicks); 

   //--- 板情報ブロードキャストを有効にする 
   if(MarketBookAdd(_Symbol)) 
   { 
      book_subscribed=true; // 成功した場合、フラグをtrueに設定
      PrintFormat("%s: MarketBookAdd(%s) function returned true",__FUNCTION__,_Symbol); 
   } 
   else 
   {
      PrintFormat("%s: MarketBookAdd(%s) function returned false! GetLastError()=%d",__FUNCTION__,_Symbol,GetLastError()); 
   }

   //--- 初期化が正常に完了 
   return(INIT_SUCCEEDED); 
} 

各部分の解説

最初のティックを待つメッセージをコメントに表示

Comment関数は、画面の左上にメッセージを表示します。 PrintFormat関数は、エキスパートログにフォーマットされたメッセージを出力します。 ここでは、最初のティックを待っていることを表示します。 I64uは、64ビット符号なし整数を表すフォーマット指定子です。この指定子を使うことで、大きな数値も正確に表示できます。

板情報ブロードキャストを有効にする

MarketBookAdd関数は、指定したシンボルの板情報イベントの購読を設定します。 成功した場合、book_subscribedフラグをtrueに設定し、成功メッセージをログに出力します。 失敗した場合、エラーメッセージをログに出力します。

初期化が正常に完了

OnInit関数は、初期化が正常に完了したことを示すためにINIT_SUCCEEDEDを返します。

フォーマット指定子について

StringFormat関数は、指定されたフォーマットに従って文字列を作成します。以下のフォーマット指定子が使用されています。

  • %I64u
    • 64ビット符号なし整数を表します。この指定子を使うことで、大きな数値も正確に表示できます。
      フォーマット指定子が%dではなく%I64uにしている意図は、変数が64ビット符号なし整数であるためです。
      %dは32ビットの整数を表すのに対し、%I64uは64ビットの符号なし整数を表します。64ビットの符号なし整数を使用することで、より大きな値を正確に扱うことができ、データの範囲が広がります。これは、大規模なデータや高精度の計算を扱う場合に特に重要です。
  • %s
    • 文字列を表します。この指定子を使うことで、関数名やシンボル名を文字列として挿入できます。

例えば、StringFormat関数で “Waiting for the first %I64u ticks to arrive” と ExtSkipFirstTicks を指定すると、ExtSkipFirstTicksの値を埋め込んだメッセージを生成します。また、PrintFormat関数で “%s: MarketBookAdd(%s) function returned true” と __FUNCTION___Symbolを指定すると、関数名とシンボル名を埋め込んだメッセージを生成します。

解説3: OnDeinit関数

//+------------------------------------------------------------------+ 
//| エキスパートの初期化を解除する関数                              | 
//+------------------------------------------------------------------+ 
void OnDeinit(const int reason) 
{ 
   //--- 初期化解除の理由コードを表示する 
   Print(__FUNCTION__,": Deinitialization reason code = ",reason);   

   //--- 板情報イベント受信の購読を取り消す 
   if(book_subscribed) 
   { 
      if(!MarketBookRelease(_Symbol)) 
      {
         PrintFormat("%s: MarketBookRelease(%s) returned false! GetLastError()=%d",__FUNCTION__,_Symbol,GetLastError()); 
      }
      else 
      {
         book_subscribed=false; // 成功した場合、フラグをfalseに設定
      }
   }
}

各部分の解説

初期化解除の理由コードを表示する

Print関数を使用して、初期化解除の理由コードをエキスパートログに表示します。
__FUNCTION__は、現在実行中の関数名を示します。
reasonは、初期化解除の理由を示す変数です。

板情報イベント受信の購読を取り消す

if文を使用して、MarketBookRelease関数戻り値を確認しています。MarketBookRelease関数がfalseを返した場合、つまり購読解除が失敗した場合の処理が行われます。

PrintFormat関数を使用してエラーメッセージをエキスパートログに出力します。この関数では、フォーマット指定子を使用して、関数名、シンボル名、およびエラーコードを出力します。__FUNCTION__は現在実行中の関数名を表し、_Symbolはシンボル名を表します。GetLastError関数は最後に発生したエラーコードを取得します。

フォーマット指定子の詳細は以下の通りです。

  • %s は文字列を表し、ここでは関数名やシンボル名に対応します。
  • %d は整数を表し、エラーコードに対応します。

エラーメッセージを出力した後、else文により購読解除が成功した場合の処理が行われます。この場合、book_subscribedフラグをfalseに設定します。

この一連の処理により、板情報イベントの購読が正しく解除されたかどうかを確認し、適切なログを出力し、フラグの状態を更新します。

解説4: OnBookEvent関数その1

//+------------------------------------------------------------------+ 
//| BookEvent関数                                                    | 
//+------------------------------------------------------------------+ 
void OnBookEvent(const string &symbol) 
{ 
   //--- テスト開始時刻を保持する変数
   static ulong starttime=0;        

   //--- 板情報更新カウンタ
   static ulong tickcounter=0;      

   //--- 板情報イベントを扱うのは、自分たちで購読している場合のみ
   if(!book_subscribed) 
      return; 

   //--- 特定シンボルのみの更新を数える
   if(symbol!=_Symbol) 
      return; 

各部分の解説

テスト開始時刻を保持する変数

static ulong starttime=0;

  • この変数は、テストの開始時刻を保持します。static修飾子が付いているため、この変数関数の呼び出し間で値を保持します。初期値は0に設定されています。

板情報更新カウンタ

static ulong tickcounter=0;

  • この変数は、板情報の更新回数をカウントします。こちらもstatic修飾子が付いているため、関数の呼び出し間で値を保持します。初期値は0に設定されています。

板情報イベントを扱うのは、自分たちで購読している場合のみ

if(!book_subscribed)
return;

  • この条件文は、book_subscribedフラグがfalseである場合に、関数の処理を終了します。つまり、板情報イベントが購読されていない場合には、何も処理を行わずに関数を抜けます。

特定シンボルのみの更新を数える

if(symbol!=_Symbol)
return;

  • この条件文は、受信したシンボルが現在のシンボルと一致しない場合に、関数の処理を終了します。これにより、現在監視しているシンボルの板情報更新のみを処理します。

解説5: OnBookEvent関数その2

 //--- 最初のティックをスキップしてカウンタをクリアする
   tickcounter++; 
   if(tickcounter<ExtSkipFirstTicks) 
      return; 

   //--- 最初のティックを受け取った時に開始時刻を記憶する
   if(tickcounter==ExtSkipFirstTicks)  
      starttime=GetMicrosecondCount(); 

   //--- 板情報データをリクエストする
   MarketBookGet(symbol,book); 

   //--- テスト終了時の時間を計算
   ulong endtime=GetMicrosecondCount()-starttime; 

各部分の解説

最初のティックをスキップしてカウンタをクリアする

tickcounter++;

  • 板情報更新カウンタを1増加させます。

if(tickcounter<ExtSkipFirstTicks)
return;

  • tickcounterがExtSkipFirstTicksより小さい場合、関数の処理を終了します。これは、最初の数回のティックを無視するための処理です。ExtSkipFirstTicksは、無視するティックの数を示す入力パラメータです。

最初のティックを受け取った時に開始時刻を記憶する

if(tickcounter==ExtSkipFirstTicks)
starttime=GetMicrosecondCount();

  • tickcounterがExtSkipFirstTicksに等しい場合、テストの開始時刻を記憶します。GetMicrosecondCount関数は、マイクロ秒単位で経過時間を取得します。この処理により、正確なテスト開始時刻が記録されます。

板情報データをリクエストする

MarketBookGet(symbol,book);

  • MarketBookGet関数を使用して、指定されたシンボルの板情報データをbook配列に取得します。これにより、最新の板情報がbook配列に格納されます。

テスト終了時の時間を計算

ulong endtime=GetMicrosecondCount()-starttime;

  • テスト終了時の時間を計算します。GetMicrosecondCount関数で現在の時間を取得し、開始時刻からの経過時間を計算します。この時間は、テストの実行時間を示します。

解説6: OnBookEvent関数その3

 //--- テストの開始からどれくらいの時間が経過したかを確認
   if(endtime>ExtCollectTime*1000*1000)  
   { 
      //--- テストの結果をログに出力
      PrintFormat("%I64u ticks for %.1f seconds: %.1f ticks/sec ",ticks,endtime/1000.0/1000.0,ticks*1000.0*1000.0/endtime); 

      //--- エキスパートを削除
      ExpertRemove(); 
      return; 
   } 

   //--- カウンタをコメント欄に表示する
   if(endtime>0) 
      Comment(StringFormat("%I64u ticks for %.1f seconds: %.1f ticks/sec ",ticks,endtime/1000.0/1000.0,ticks*1000.0*1000.0/endtime)); 

各部分の解説

テストの開始からどれくらいの時間が経過したかを確認

if(endtime>ExtCollectTime1000*1000)

  • ExtCollectTime(秒単位でのテスト時間)をマイクロ秒(100万分の1秒)単位に変換するために1000を2回掛けています。endtimeがこの変換後の時間よりも大きい場合、テストの実行時間が指定された時間を超えたことを意味します。

テストの結果をログに出力

PrintFormat(“%I64u ticks for %.1f seconds: %.1f ticks/sec “,ticks,endtime/1000.0/1000.0,ticks1000.01000.0/endtime);

  • テスト結果をエキスパートログに出力します。
  • ticksは、テスト中に記録されたティックの総数です。
  • endtimeを1000で2回割ることで、マイクロ秒を秒に変換します。
    ※endtime/1000.0により、マイクロ秒単位の時間がミリ秒単位に変換されます。
    その結果をさらに/1000.0することで、ミリ秒単位の時間が秒単位に変換されます。
  • ticksを秒数で割ることで、1秒あたりのティック数を計算します。
  • %.1fは浮動小数点数を小数点以下1桁まで表示するフォーマット指定子です。

エキスパートを削除

ExpertRemove();

カウンタをコメント欄に表示する

if(endtime>0)
Comment(StringFormat(“%I64u ticks for %.1f seconds: %.1f ticks/sec “,ticks,endtime/1000.0/1000.0,ticks1000.01000.0/endtime));

  • endtimeが0より大きい場合、テスト結果を画面のコメント欄に表示します。
  • ticks、endtime、秒数あたりのティック数を表示します。
  • 四則演算は前述と同様で、マイクロ秒を秒に変換し、ティック数を秒数で割って1秒あたりのティック数を計算しています。
    ※ticksはティックの総数です。ticksを1000.0*1000.0(すなわち100万)倍することで、ティック数をマイクロ秒単位に換算します。この値をendtime(マイクロ秒単位の経過時間)で割ることで、1秒あたりのティック数を計算します。
  • この計算により、実行時間の中でどれだけのティックが発生したかを1秒あたりの数値として求めることができます。
タイトルとURLをコピーしました