2018年5月23日水曜日

1分足のバーが確定したタイミングで実行するコード

ご購入者様より以下のご質問を頂きましたので、その回答の記事となります。

「コミュニティにて1分足が確定したタイミングで判定していると記述されておりましたが、どういったコードで書けるのでしょうか。」
(EA開発の勉強をなされている方の質問となります)

「単純に一分毎に実行」するのではなく、「1分足のバーが確定したタイミングで実行」する為のコードは以下となります。


//! @class  GoodExample
class GoodExample
{
private:
    string mCurrencyPairName;
    datetime mBeforeBarCreationDateTime;

public:
    GoodExample(string currencyPairName)
    :   mCurrencyPairName(currencyPairName),
        mBeforeBarCreationDateTime(0)
    {
    }

    //! @brief  1分毎の処理。
    void On1Minute()
    {
        Print("On1Minute");
    }

    //! @brief  ティック毎の処理。
    void OnTick()
    {
        // 最新の1分足のバーの形成開始時刻を取得。
        datetime current = iTime(mCurrencyPairName, PERIOD_M1, 0);
        
        // 前のティックでの形成開始時刻と比較。
        if (current != mBeforeBarCreationDateTime)
        {
            // 違うならば前のバーが確定し新しいバーになった=1分毎の更新タイミング。
            On1Minute();

            // バーの形成開始時刻を更新。
            mBeforeBarCreationDateTime = current;
        }
    }
};

GoodExample gGoodExample(_Symbol);

void OnTick()
{
    gGoodExample.OnTick();
}


iTime()により1分足のバーの形成開始時刻を取得し、それが切り替わったタイミングで判定します。
このままですと、起動した一番最初に1分足が確定したタイミングとは別に一度実行されてしまいますので、不都合がある場合は以下の様にコンストラクタでしっかりと初期化しておくと更に良いです。

    GoodExample(string currencyPairName)
    :   mCurrencyPairName(currencyPairName)
    {
        mBeforeBarCreationDateTime = iTime(mCurrencyPairName, PERIOD_M1, 0);
    }
1分毎の処理、5分毎の処理、15分毎の処理…とそれぞれ用意しておくと便利かもしれません。

また、以下のコードでも同様の動作が行えそうに見えるのですが、こちらは良くない例です。

//! @class  BadExample
class BadExample
{
private:
    string mCurrencyPairName;
    int mBeforeCountOfBars;

public:
    BadExample(string currencyPairName)
    :   mCurrencyPairName(currencyPairName),
        mBeforeCountOfBars(0)
    {
    }

    //! @brief  1分毎の処理。
    void On1Minute()
    {
        Print("On1Minute");
    }

    //! @brief  ティック毎の処理。
    void OnTick()
    {
        // 最新の1分足のバーの数を取得。
        int current = iBars(mCurrencyPairName, PERIOD_M1);
        
        // 前のティックでの数と比較。
        if (current > mBeforeCountOfBars)
        {
            // 違うならば新しいバーが追加された=1分毎の更新タイミング。
            On1Minute();

            // バーの数を更新。
            mBeforeCountOfBars = current;
        }
    }
};

バーの数で判定しているのですが、バーの数はMT4の設定の「ヒストリー内の最大バー数」・「チャートの最大バー数」などによって最大値が決まっており、その値に達すると(確か)一定量古いバーを削除するので、減る場合があります。
「>」を「!=」に変えたらそれなりにうまくいくかも知れませんが、MT4側の挙動に振り回される為、基本的にiBars()を用いた判定は行わない方が良いです。
これはバックテストでは検出できず、実際の口座で動かしてから判明する不具合に繋がるので、結構怖い例だったりします。