メインコンテンツへスキップ
見出し画像

MT5のEA開発をあきらめた方へのヒント💡①(2つの障壁を克服せよ!)

    1.MT5のEA開発で戸惑うこと

    (1)戸惑っている人の傾向

    MT4の開発経験がある方や、実際にMT4でEAを開発して自身で動かしたり、販売したりされてる方だと思います。
    なぜならMT5のEAから開発を始めた方は、「こんなもんなんか」と仕様に対して疑問や不満は発生しないと思うからです。比較対象がないからです。

    私もMT4でEAを5年以上開発し、自身で運用してきたのでMT5のEAを開発する際にめちゃくちゃ戸惑いました。MT4での開発経験が長ければ長いほどこの戸惑いは大きくなると思います。なので私が感じた戸惑いとその解消方法を同じ悩みに直面する方々に届けたいと思います。

    (2)MT5、MQL5 EAのメリット

    MT5のEA開発は正直に言うとMT4でのEA開発より面倒臭いです。なので何かしらのモチベーションがなければ続かず挫折しかねません。EAをMT5化することのメリットをモチベーションの1つにしましょう。

    《 MT5、MT5 EAのメリット 》
    ①バックテスト、最適化機能がMT4を圧倒・凌駕している
    ②MT4と異なりバックテストでもプログラムのデバッグが可能
    ③MT4と異なり非同期通信(一括決済等)できるのでEAの性能が上がる
    ④MT4と異なりDBへネイティブ接続できるのでEAの幅が広がる

    2.MT4との違いを理解しよう

    MQL5のコードの書き方などの情報は巷に溢れています。でも視点がMT5・MQL5を軸にしているので、MT4・MQL4を長く使ってきた人にはイマイチ届かない・響かないんです。これは私自身が数年前に実感したことです。

    結論から言うとMT4開発者がMT5開発において戸惑うポイントは3つ。

    《 MQL4とMQL5を比較 》
    ①データ構造の激変
    ②インジゲーター関数の扱い方の激変
    ③価格・日付時刻の扱い方の変化

    ③は慣れで解決できますが、①②の激変に対して「何でこうしたん?」「超めんどい!」という気持ちが前面に出ます。私はそうでした。おそらく他の方も同様に思うはずです。それでは個別に見ていきましょう。

    3.MQL4とMQL5の違い

    (1)データ構造が激変(2つの障壁のひとつ)

    MQL4では2つだけだったデータ(情報)がMQL5では4つに増加しました。巷に溢れているMQL5の解説は、この大事な部分の解説が抜け落ちているので、解説が腑に落ちない・入ってこないんですよね。私はそうでした。

    画像
    データ構造比較表

    まず、注文から決済までの流れをMQL4/MQL5の視点で捉えることからはじめましょう。一般的な成行注文&決済で考えます。
    ➊成行注文→❷注文約定→❸ポジション→❹成行決済→➎決済約定
    上記流れになります。これはMT4/MT5ともに同じです。


    MQL4(MT4)では上述した「データ①~②」と「成行注文&決済➊~➎」の紐付けは以下になります。

    • ①取引情報:❸

    • ②取引履歴情報:❸+➎ ※オープン時+クローズ時の情報が1レコード

    ※➊❷❹はデータとして存在しない


    MQL5(MT5)では上述した「データ①~④」と「成行注文&決済➊~➎」の紐付けは以下になります。

    • ①注文情報:なし

    • ②約定履歴情報:❷➍ ※1つの注文でINとOUTの2レコード生成

    • ③ポジション情報:❸ ※クローズされるとここから無くなる

    • ④注文履歴情報:❸

    ※➊はデータとして存在しない(➊は指値/逆指値の場合にのみ存在)


    既に頭から湯気が出てきてると思います。
    MQL5(MT5)複雑すぎですよね。しかもデータ構造(①~④)が直感的でなく非常に分かり難いのです。
    ※成行注文の場合、②のINと④はほぼ同じ情報(内容)になります・・・

    ポジションが決済されたら、ポジション履歴情報にデータを保持してくれ!と考えちゃいますが、ポジション履歴情報はないのです・・・うそーん。
    なのでMQL5では、過去ポジションの情報をプログラムで取得しようとすると死ぬほど面倒臭くなるのです( ;∀;)


    MQL4のコード(主要部分のコードのみ記述)
    ①取引情報の取得(OrderSelect)

    bool blnRet = OrderSelect(intIndex, SELECT_BY_POS, MODE_TRADES);

    ②取引履歴情報の取得(OrderSelect)

    bool blnRet = OrderSelect(intTicket, SELECT_BY_TICKET, MODE_HISTORY);

    非常にシンプルです。
    OrderSelectという関数1つで取引情報、取引履歴情報を取得できます。


    MQL5のコード(主要部分のコードのみ記述)

    ①注文情報の取得(OrderGetTicket / OrderSelect)
    ※約定されていない指値/逆指値等の注文しか取得できません

    bool blnRet = OrderGetTicket(intIndex);
    
    もしくは
    
    bool blnRet = OrderSelect(ulngTicket);

    ②約定履歴情報の取得(HistoryDealGetTicket / HistoryDealSelect)

    bool blnRet = HistoryDealGetTicket(intIndex);
    
    もしくは
    
    bool blnRet = HistoryDealSelect(ulngTicket);

    ③ポジション情報の取得(PositionGetTicket / PositionSelectByTicket)

    bool blnRet = PositionGetTicket(intIndex);
    
    もしくは
    
    bool blnRet = PositionSelectByTicket(ulngTicket);

    ④注文履歴情報の取得(HistoryOrderGetTicket / HistoryOrderSelect)

    bool blnRet = HistoryOrderGetTicket(intIndex);
    
    もしくは
    
    bool blnRet = HistoryOrderSelect(ulngTicket);

    いかがでしょうか?
    MQL5(MT5)面倒臭い。そう思うのが普通だと思います。

    MQL4(MT4)は、データの線引きが
    ・決済(クローズ)されていない → 取引情報
    ・決済(クローズ)されている  → 取引履歴情報

    決済されていない/決済されている 線引きが1つなので非常に直感的でわかりやすかったのです。

    一方、MQL5(MT5)は、データの線引きが
    ・注文保有中    → 注文情報(指値/逆指値注文)
    ・約定した     → 約定履歴情報(IN/OUTの2レコード生成される)
    ・ポジション保有時 → ポジション情報(クローズしたらなくなる)
    ・約定された注文  → 注文履歴情報

    上記のように4つもあり、かつ、直感的ではありません。
    この分かり難さがMQL5でEAを開発する人にとって最大の障壁になっていると思います。でもこれ、慣れるしかないのです・・・。

    (2)インジゲーター関数の扱い方の激変(2つの障壁のひとつ)

    データ構造の激変の次にそびえたつ障壁は、インジゲーター関数の扱い方の激変です。どう変わったかを端的に言いますと
    ①利用するインジケーター関数は事前に宣言してね(OnInit)
    ②インジケーターは事前宣言してCopyBufferで値をとってね(OnTick)

    です。

    MQL4(MT4)では、インジゲーターの値はOnTickイベントで好きな時に都度取得する仕様でした。

    ↓以下の例はボリンジャーバンドの上下ラインの値を取得しています。

    void OnTick()
    {
        double dblPriceUpp = 
            iBands("USDJPY",PERIOD_M15,15,2.0,0,PRICE_CLOSE,MODE_UPPER,0);
    
        double dblPriceLow = 
            iBands("USDJPY",PERIOD_M15,15,2.5,0,PRICE_CLOSE,MODE_LOWER,0);
    }


    一方、MQL5(MT5)では、使用するインジゲーターをOnInitイベントで宣言しOnTickイベントでCopyBufferを使って最新値をとる仕様に変わりました。

    //グローバル変数
    //ハンドル宣言
    int gintHndBandUpp = INVALID_HANDLE; //ハンドル値格納用
    int gintHndBandLow = INVALID_HANDLE; //ハンドル値格納用
    
    int OnInit()
    {
        //利用するインジケーターの事前宣言
        gintHndBandUpp = iBands("USDJPY",PERIOD_M15,15,0,2.0,PRICE_CLOSE);
        gintHndBandLow = iBands("USDJPY",PERIOD_M15,15,0,2.5,PRICE_CLOSE);
    }
    void OnTick()
    {
        int intDataCnt = 0;
        double aryBuff[];
        double dblPriceUpp = 0;
        double dblPriceLow = 0;
    
        intDataCnt = CopyBuffer(gintHndBandUpp,MODE_UPPER,0,1,aryBuff);
        if (intDataCnt > 0) {
            dblPriceUpp = aryBuff[0];
        }
    
        intDataCnt = CopyBuffer(gintHndBandLow,MODE_LOWER,0,1,aryBuff);
        if (intDataCnt > 0) {
            dblPriceLow = aryBuff[0];
        }
    }
    void OnDeinit(const int reason)
    {
        //ハンドル削除
        IndicatorRelease(gintHndBandUpp);
        IndicatorRelease(gintHndBandLow);
    }

    この仕様の面倒臭ささは、インジケーターのパラメーターが異なる場合、それぞれに事前定義が必要になるということです。
    上記例では、iBands(ボリンジャーバンド)の5番目の引数である標準偏差の値が2.0と2.5で異なるため、2つ事前定義を行い、OnTickでも明確に使い分けています。

    MQL5の正式な作法に準拠せず、以下のようにラッピングしてMQL4と同じようにOnTickのみで使用する方法もあります。
    ※メイン以外のコードは省略(TFMigrate、PriceMigrate、CopyBufferMQL4)

    double iBandsMQL4(string symbol,
                      int tf,
                      int period,
                      double deviation,
                      int bands_shift,
                      int method,
                      int mode,
                      int shift)
    {
       ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
       ENUM_APPLIED_PRICE ma_method=PriceMigrate(method);
       //ENUM_MA_METHOD ma_method=MethodMigrate(method);
       int handle=iBands(symbol,timeframe,period,
                         bands_shift,deviation,ma_method);
       if(handle<0)
         {
          Print("The iBands object is not created: Error",GetLastError());
          return(-1);
         }
       else
          return(CopyBufferMQL4(handle,mode,shift));
    }

    ↓詳細は以下のサイトを参照


    (3)価格・日付時刻の扱い方の変化

    ①価格
    MQL4(MT4)では、価格取得はシンプルでした。

    double dbpAsk = Ask;
    double dblBid = Bid;

    一方、MQL5(MT5)では、Ask/Bidという定義済み変数は廃止され、以下のコードが推奨されています。この点はそんなに違和感はないと思います。

    MqlTick tick;
    
    if (SymbolInfoTick("USDJPY",tick) == false) {
        return;
    }
    
    double dbpAsk = tick.ask; //説明するためのコード
    double dblBid = tick.bid; //説明するためのコード


    ②日付時刻
    MQL4(MT4)では、日付関数を使う仕様でした。

    datetime dteNow = TimeCurrent();      //サーバ日時を取得
    
    intYear = TimeYear(dteNow);           //サーバ日時より年を取得
    intMonth = TimeMonth(dteNow);         //サーバ日時より月を取得
    intDay = TimeDay(dteNow);             //サーバ日時より日を取得
    intHour = TimeHour(dteNow);           //サーバ日時より時を取得
    intMinute = TimeMinute(dteNow);       //サーバ日時より分を取得
    intSeconds = TimeSeconds(dteNow);     //サーバ日時より秒を取得
    intDayOfWeek = TimeDayOfWeek(dteNow); //サーバ日時より曜日を取得

    一方、MQL5(MT5)では、日付関数が多数削除され、MqlDateTime型を使用する仕様に変更されました。こちらもそこまで違和感はないと思います。

    MqlDateTime mdt;
    
    TimeCurrent(mdt);               //サーバ日時を取得
    
    intYear = mdt.year;             //サーバ日時より年を取得
    intMonth = mdt.mon;             //サーバ日時より月を取得
    intDay = mdt.day;               //サーバ日時より日を取得
    intHour = mdt.hour;             //サーバ日時より時を取得
    intMinute = mdt.min;            //サーバ日時より分を取得
    intSeconds = mdt.sec;           //サーバ日時より秒を取得
    intDayOfWeek = mdt.day_of_week; //サーバ日時より曜日を取得

    4.最後に

    いかがだったでしょうか?MT5(MQL5)のEA開発に挫折した人は2つの障壁である『①データ構造の激変』『②インジゲーター関数の扱い方の激変』が大きく影響したのではと思います。

    今後は、MT5のEAを作成するためのノウハウをnoteで発信していく予定です。気になる方はフォロー、いいね、よろしくお願い致します。

    あなたへのおすすめ