MT5で新しいバーができたタイミングを判別する関数

# はじめに
MetaTrader(MT4,MT5)で動作する自動売買ソフト(EA)では、価格が変わるタイミング（ティック）ごとにトレードさせることができますが、場合によっては、新しいバーができたタイミングだけでトレードさせたいこともあります。またその方が、始値のみのモデルでバックテストできて便利だったりします。

本記事は、新しいバーができたタイミングを判別する関数についてのメモです。

# 参考文献
元ネタは以下の電子書籍です。参考といっても、拙著なので宣伝です。
[『メタトレーダー４＆５共通ライブラリによるＥＡ開発入門』](https://amzn.to/33JHotW)

# もとのisNewBar()
参考文献の共通ライブラリに記述されている関数です。シンボルとタイムフレームを引数として、そのチャートで新しいバーができたときに`true`を返す関数です。

````
//新規バーのチェック
bool isNewBar(string symbol, ENUM_TIMEFRAMES tf)
{
   static datetime time = 0;
   if(iTime(symbol, tf, 0) != time)
   {
      time = iTime(symbol, tf, 0);
      return true;
   }
   return false;
}
````

特にこの方法が目新しいわけではなく、ネットに転がっているのと似たようなものです。ただ、以前のMQL5では`iTime()`がなかったので、MT4とMT5とで別々に作らなくてはいけなかったのですが、現在のバージョンでは、MT4とMT5のどちらでも使えます。

`iTime(symbol, tf, 0)`で最新のバーの時刻を取得しますが、これは、最新のバーが変化していても常に始値の時刻を表します。なので、これが変われば新しいバーができたと判別できる仕組みです。

#isNewBar()の使い方
##普通の使い方
通常、`isNewBar()`はティックごとに動作する関数のなかで１回だけ使うことが多いでしょう。例えば、１分足のバーができたときに"newbar1"と表示させたい場合、

````
void OnTick()
{
   if(isNewBar(_Symbol, PERIOD_M1)) Print("newbar1");
}
````
と書けばいいだけです。Print関数をトレード関数に置き換えれば、新しいバーができたときだけトレードさせることができます。

##普通でない使い方
普通でないというわけではありませんが、自分でコード書いていて気が付いただけです。同じティック関数中に`isNewBar()`を２回以上使った場合です。

````
void OnTick()
{
   if(isNewBar(_Symbol, PERIOD_M1)) Print("newbar1");
   Sleep(100);
   if(isNewBar(_Symbol, PERIOD_M1)) Print("newbar2");
}
````

これは一例です。`Sleep(100)`のところでは、ティックベースの処理を行いますが、その前後では、バーができたタイミングで処理したい場合などです。

この場合、関数の仕様上、１回目の`isNewBar()`では、`true`を返すため、"newbar1"は表示されますが、２回目の`isNewBar()`では、`false`となり、"newbar2"は表示されません。

ただ、心情的には、このティックは新しいバーができたタイミングなので、２回目の呼び出しでも`true`を返してほしいところです。

#新しいisNewBar()
そこで、上記の普通でない使い方にも対応できるよう`isNewBar()`を修正してみます。

利用したのは、ティックの情報を取得する`SymbolInfoTick()`という関数です。これは、シンボルを引数として入力し、直近のティック情報を２番目の引数で`MqlTick`という構造体として返す関数です。

この`MqlTick`という構造体は、MQL4とMQL5とでメンバーがちょっと異なります。

MQL4： https://docs.mql4.com/constants/structures/mqltick
MQL5： https://www.mql5.com/ja/docs/constants/structures/mqltick

このなかで、時間に関係するメンバーとして、`time`(MQL4とMQL5の両方）と`time_msc`(MQL5のみ)があります。この違いは、`time`が秒単位なのに対して、`time_msc`がミリ秒単位ということです。

`time`の方がMQL4とMQL5の両方で使えるのでいいのですが、秒単位だと、同じ秒で複数のティックが発生することもよくあります。やはり、ティックを区別するためには、ミリ秒単位の情報が必要となります。

そこで、MT5に限定ですが、`time_msc`を使って`isNewBar()`を修正してみます。

````
//新規バーのチェック
bool isNewBar(string symbol, ENUM_TIMEFRAMES tf)
{
   static datetime bartime = 0;
   static long ticktime = 0;
   MqlTick tick;
   SymbolInfoTick(symbol, tick);
   if(iTime(symbol, tf, 0) != bartime)
   {
      bartime = iTime(symbol, tf, 0);
      ticktime = tick.time_msc;
      return true;
   }
   else if(ticktime == tick.time_msc) return true;
   return false;
}
````
`static`変数として、バーの開始時刻を表す`bartime`に加えて、ティックの時刻を表す`ticktime`という変数を用意します。そして、`tick`という変数にティック情報を取得し、新しいバーができたときに、その時刻`tick.time_msc`を`ticktime`に記憶しておきます。

こうすることで、同じティックで`isNewBar()`が２回目以降呼び出された場合でも、ティック時刻が同じであれば、新しいバーができたタイミングだとみなし、`true`を返します。

これを使うと、さっきの`isNewBar()`を２回呼び出す場合でも、"newbar1"と"newbar2"の両方を表示させることができます。

#おわりに
今回の記事で紹介した`isNewBar()`ですが、MQL4では使えないので、参考文献の共通ライブラリには、当面は採用しません。同じティックで`isNewBar()`を２回以上呼びたい場合、

````
void OnTick()
{
   bool flag_newbar = isNewBar(_Symbol, PERIOD_M1);
   if(flag_newbar) Print("newbar1");
   Sleep(100);
   if(flag_newbar) Print("newbar2");
}
````

のように`isNewBar()`の戻り値を記憶しておけばよいでしょう。

久々のQiita記事らしく、たいていの人には関係ない情報をお届けしました。お粗末様でした。