はじめに
MetaTrader(MT4,MT5)で動作する自動売買ソフト(EA)では、価格が変わるタイミング(ティック)ごとにトレードさせることができますが、場合によっては、新しいバーができたタイミングだけでトレードさせたいこともあります。またその方が、始値のみのモデルでバックテストできて便利だったりします。
本記事は、新しいバーができたタイミングを判別する関数についてのメモです。
参考文献
元ネタは以下の電子書籍です。参考といっても、拙著なので宣伝です。
『メタトレーダー4&5共通ライブラリによるEA開発入門』
もとの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()
はティックごとに動作する関数のなかで1回だけ使うことが多いでしょう。例えば、1分足のバーができたときに"newbar1"と表示させたい場合、
void OnTick()
{
if(isNewBar(_Symbol, PERIOD_M1)) Print("newbar1");
}
と書けばいいだけです。Print関数をトレード関数に置き換えれば、新しいバーができたときだけトレードさせることができます。
普通でない使い方
普通でないというわけではありませんが、自分でコード書いていて気が付いただけです。同じティック関数中にisNewBar()
を2回以上使った場合です。
void OnTick()
{
if(isNewBar(_Symbol, PERIOD_M1)) Print("newbar1");
Sleep(100);
if(isNewBar(_Symbol, PERIOD_M1)) Print("newbar2");
}
これは一例です。Sleep(100)
のところでは、ティックベースの処理を行いますが、その前後では、バーができたタイミングで処理したい場合などです。
この場合、関数の仕様上、1回目のisNewBar()
では、true
を返すため、"newbar1"は表示されますが、2回目のisNewBar()
では、false
となり、"newbar2"は表示されません。
ただ、心情的には、このティックは新しいバーができたタイミングなので、2回目の呼び出しでもtrue
を返してほしいところです。
新しいisNewBar()
そこで、上記の普通でない使い方にも対応できるようisNewBar()
を修正してみます。
利用したのは、ティックの情報を取得するSymbolInfoTick()
という関数です。これは、シンボルを引数として入力し、直近のティック情報を2番目の引数で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()
が2回目以降呼び出された場合でも、ティック時刻が同じであれば、新しいバーができたタイミングだとみなし、true
を返します。
これを使うと、さっきのisNewBar()
を2回呼び出す場合でも、"newbar1"と"newbar2"の両方を表示させることができます。
おわりに
今回の記事で紹介したisNewBar()
ですが、MQL4では使えないので、参考文献の共通ライブラリには、当面は採用しません。同じティックでisNewBar()
を2回以上呼びたい場合、
void OnTick()
{
bool flag_newbar = isNewBar(_Symbol, PERIOD_M1);
if(flag_newbar) Print("newbar1");
Sleep(100);
if(flag_newbar) Print("newbar2");
}
のようにisNewBar()
の戻り値を記憶しておけばよいでしょう。
久々のQiita記事らしく、たいていの人には関係ない情報をお届けしました。お粗末様でした。