このプロジェクトは、収益性の高いトレーディングロボットを作成する手助けになります! 少なくとも、そうなるでしょう。

MetaQuotes | 15 6月, 2020

トレーディングロボットの作成は、常に小さなファイルから始まり、追加の関数やカスタムオブジェクトを実装するにつれてサイズが大きくなります。 ほとんどの MQL5 プログラマーはインクルードファイル(MQH) を使用してこの問題を処理します。 しかし、より良い解決策があります。:それは、プロジェクト内の任意のトレードアプリケーションの開発を開始することです。 そうする理由はたくさんあります。


プロジェクトのメリット

プロジェクトは、MQPROJ 拡張子を持つ独立したファイルで、プログラム設定、コンパイル パラメータ、およびプロジェクトで使用するすべてのファイルに関する情報を格納します。 プロジェクトを操作するために、Navigator の別のタブが用意されています。 インクルード、リソース、ヘッダ、その他のファイルなど、すべてのファイルは、このタブのカテゴリに分類されます。

プロジェクトの例

プロジェクトは、別のディレクトリに配置されたファイルやフォルダのセットではありません。 バランスのとれた構造に配置された要素に複雑なプログラムを分解することができます。 必要な情報はすべて手元にあります。

プログラムパーツ間のすべての接続はプロジェクト内で明確に表示されるため、すべての使用ファイル間をナビゲートできます。 さらに、複数のプログラマが組み込みのMQL5 Storageを介してプロジェクトで共同機能することができます。


プロジェクトの作成

MQL5ウィザードを使用して、MQL5の新しいプロジェクトを作成します。[new project] をクリックし、プログラム名の設定、インプットパラメータの追加、および使用するイベント ハンドラの指定という必要なステップを実行します。 MQL5 ウィザードが完了すると、MQPROJ ファイルが開きます。 このファイルでは、プロジェクトのプロパティを管理できます。

プロジェクトプロパティ>


ここでは、バージョンの指定、プログラムの説明の設定、アイコンの追加、追加オプションの管理を行うことができます。

  1. 最大最適化 — 最大パフォーマンスを実現する.ex5 実行可能ファイルの最適化。 このオプションが無効になっている場合、ソース コードのコンパイルは高速に完了できますが、結果として生成される.ex5 ファイルの実行速度は大幅に低下します。
  2. 浮動小数点の除算をチェック — 倍精度浮動小数点型の実数が除算演算でゼロでないかどうかを確認します。 オプションが無効になっている場合、操作速度は高くなります。 ただし、コーディングに完全に自信がある必要があります。
  3. テスター最適化キャッシュを使用する — テスターはデフォルトで有効になっており、完了したパスの結果をすべて最適化キャッシュに保存します。 このデータは、再計算に使用することができます。 キャッシュは、tester_no_cacheプロパティを使用して無効にすることができます。 必要に応じて、プロジェクト内の該当するオプションのチェックを外すことができます。

プロジェクト ファイルが閉じている場合は、[プロパティ] コンテキスト メニューの適切なコマンドを使用して、プロジェクト ファイルを再度開くことができます。 MQPROJ ファイルの内容を深く理解するために、Open コマンドを使用して、ファイルをテキスト形式で開くことができます。 したがって、プロジェクトの内部構造を表示できます。

{
  "platform"    :"MetaTrader5",
  "program_type":"expert",
  "copyright"   :"Copyright 2019, MetaQuotes Software Corp.",
  "link"        :"https:\/\/www.mql5.com",
  "version"     :"1.00",
  "description" :"平均リバース戦略:価格がチャネルボーダーを外側にブレイクし、平均に戻ります。 チャンネルはボリンジャーバンドで表されます。 このエキスパートアドバイザーは、トレンド方向でのみ開くことができるリミット注文を使用して相場にエントリーします。"
  "icon"        :"Mean Reversion.ico",
  "optimize"    :"1",
  "fpzerocheck" :"1",
  "tester_no_cache":"0",
  "tester_everytick_calculate":"0",

  "files":
  [
    {
      "path":".\\Mean Reversion.mq5",
      "compile":"true",
      "relative_to_project":"true"
    },
    {
      "path":"MQL5\\Include\\Trade\\Trade.mqh",
      "compile":"false",
      "relative_to_project":"false"
    },
....


    トレードルール

    古典的なルールを適用してみましょう:価格がBollinger Bandに触れたときにエントリーします。 価格がその平均値に戻ることを期待した、トレード戦略の一つです。

    ボリンジャーバンドに基づく相場エントリー

    相場エントリーには、リミットオーダーのみを使用します。 追加のルールは次のようになります: トレンド方向にのみトレード. したがって、買いリミットは上昇トレンドの下チャネル境界に配置されます。 下降トレンドの間、売りリミットオーダーは上の境界線に置かれます。

    トレンドの方向を決定する方法はたくさんあります。 最もシンプルなものを使いましょう。:2つの移動平均の相対ポジションです。 高速 EMA が低速 EMA を上回っている場合は、上昇トレンドとして定義されます。 下降トレンドは、線が逆の方向に配置されている場合に認識されます。

    2つの移動平均を使用したトレンドの決定

    このシンプルなルールには、常に上昇トレンドまたは下降トレンドがあるという1つの欠点があります。 したがって、このようなシステムはレンジ時に多くのダマシエントリーに引っ掛かります。 これを避けるために、ボリンジャーバンド間の距離が十分に大きいときに予約オーダーを置くことを可能にする別のルールを追加します。 最適な方法は、ポイントではなく相対値でチャネル幅を測定することです。 この幅は、ポイントのボラティリティを測定するATRインジケーターに基づいて決定することができます。

    ここでkは、見つけられる必要がある特定の係数です。

    ATR を使用したボリンジャー チャネル幅の計算


    したがって、プロジェクトの作成時に、トレードシグナルを決定するための8つのインプットパラメータを指定する必要があります。 このEAは、InpLotパラメータで指定する必要がある固定ロットをトレードします。 最適化できないもう 1 つのパラメータは、InpMagicNumberです。 これを使用することで、EAに対して、独自のオーダーとポジションのみを処理するように指示することができます。

    //--- チャネルパラメータ
    input int             InpBBPeriod   =20;           // ボリンジャーバンド期間
    インプットダブルInpBBDeviation=2.0;         // MAからのボリンジャーバンドの偏差
    //-- トレンド計算のEMA期間 
    input int             InpFastEMA    =12;           // 短期EMA期間
    input int             InpSlowEMA    =26;           //長期EMA期間
    //-- ATR パラメータ
    input int             InpATRPeriod  =14;           // ATR期間
    input double          InpATRCoeff   =1.0;          //レンジを決定するためのATR 係数
    //---資本管理
    input double          InpLot        =0.1;          // トレードボリューム
    //--- 時間枠のパラメータ
    input ENUM_TIMEFRAMES InpBBTF       =PERIOD_M15;   //ボリンジャー値計算の時間枠
    input ENUM_TIMEFRAMES InpMATF       =PERIOD_M15;   //トレンド決定の時間枠
    //---トレードトレードのEA識別子
    input long            InpMagicNumber=245600;       // マジックナンバー
    
    

    トレンドとチャネル幅を決定するための時間枠の手動選択を避けるために、InpBBTFとInpMATFパラメータが追加されました。 この場合、最適化中に最適な時間枠値を見つけることができます。 M15からM30の移動平均とボリンジャーバンドデータを使用しながら、このEAをM1時間枠で実行することができます。 ATR にはインプットパラメータは使用されませんが、それ以外の場合は、この例では多くのパラメータを使用します。


    関数の記述

    プロジェクトを作成したら、EAの開発に進むことができます。 以下のコードは、ルールを説明する主な3つの関数を示します。

    ボリンジャーチャネル幅の計算は簡単です:インジケータバッファから値をコピーします。

    //+------------------------------------------------------------------+
    //|チャネル境界の値を取得する |
    //+------------------------------------------------------------------+
    bool ChannelBoundsCalculate(double &up, double &low)
      {
    //---ボリンジャーバンドのインジケータ値を取得 
       double bbup_buffer[];
       double bblow_buffer[];
       if(CopyBuffer(ExtBBHandle, 1, 1, 1, bbup_buffer)==-1)
         {
          PrintFormat("%s: Failed CopyBuffer(ExtBBHandle,0,1,2,bbup_buffer), code=%d", __FILE__, GetLastError());
          return(false);
         }
    
       if((CopyBuffer(ExtBBHandle, 2, 1, 1, bblow_buffer)==-1))
         {
          PrintFormat("%s: Failed CopyBuffer(ExtBBHandle,0,1,2,bblow_buffer), code=%d", __FILE__, GetLastError());
          return(false);
         }
       low=bblow_buffer[0];
       up =bbup_buffer[0];
    //--- 成功
       return(true);
      }
    
    

    レンジ判定方法も簡単です。 まず、チャネル境界の値を取得し、次に幅を計算し、ATR 値に InpATRCoeff 係数を掛けた値と比較します。

    //+------------------------------------------------------------------+
    //| チャネルが狭すぎる場合に true を返す (レンジ表示) |
    //+------------------------------------------------------------------+
    int IsRange()
      {
    //--- 最後に完了した足の ATR 値を取得
       double atr_buffer[];
       if(CopyBuffer(ExtATRHandle, 0, 1, 1, atr_buffer)==-1)
         {
          PrintFormat("%s: Failed CopyBuffer(ExtATRHandle,0,1,2,atr_buffer), code=%d", __FILE__, GetLastError());
          return(NO_VALUE);
         }
       double atr=atr_buffer[0];
    //--- チャネルの境界線を取得
       if(!ChannelBoundsCalculate(ExtUpChannel, ExtLowChannel))
          return(NO_VALUE);
       ExtChannelRange=ExtUpChannel-ExtLowChannel;
    //---チャネル幅が ATR*係数より小さい場合はレンジ
       if(ExtChannelRange<InpATRCoeff*atr)
          return(true);
    //---レンジが検出されないなら
       return(false);
      }
    
    

    コードからわかるように、NO_VALUEマクロ コードが返されることがあり、特定のパラメータの計算が失敗したことを意味します。

    #define NO_VALUE      INT_MAX                      //シグナルまたはトレンドの計算時に無効な値

    トレンド判定関数は最も長いコードです。

    //+------------------------------------------------------------------+
    //|上昇トレンドの場合は 1 を、下降トレンドの場合は -1 を返す (0 = トレンドなし) |
    //+------------------------------------------------------------------+
    int TrendCalculate()
      {
    //---最初にレンジをチェック 
       int is_range=IsRange();
    //--- 結果を確認
       if(is_range==NO_VALUE)
         {
          //--- チェックが失敗した場合は、"no value"で早期終了
          return(NO_VALUE);
         }
    //--- レンジ時は方向を決定しない
       if(is_range==true) //狭いレンジでは"flat"を返す
          return(0);
    //--- 最後に完了した足の ATR 値を取得
       double atr_buffer[];
       if(CopyBuffer(ExtBBHandle, 0, 1, 1, atr_buffer)==-1)
         {
          PrintFormat("%s: Failed CopyBuffer(ExtATRHandle,0,1,2,atr_buffer), code=%d", __FILE__, GetLastError());
          return(NO_VALUE);
         }
    //---最後に完了した足の短期EMA 値を取得。
       double fastma_buffer[];
       if(CopyBuffer(ExtFastMAHandle, 0, 1, 1, fastma_buffer)==-1)
         {
          PrintFormat("%s: Failed CopyBuffer(ExtFastMAHandle,0,1,2,fastma_buffer), code=%d", __FILE__, GetLastError());
          return(NO_VALUE);
         }
    //---最後に完了した足の長期EMA 値を取得
       doubleSLowma_buffer[];
       if(CopyBuffer(ExtSlowMAHandle, 0, 1, 1,SLowma_buffer)==-1)
         {
          PrintFormat("%s: Failed CopyBuffer(ExtSlowMAHandle,0,1,2,slowma_buffer), code=%d", __FILE__, GetLastError());
          return(NO_VALUE);
         }
    //---トレンドはデフォルトでは定義されていません
       int trend=0;
    //--- 短期EMAが長期EMAの上にある場合
       if(fastma_buffer[0]>slowma_buffer[0])
          trend=1;   // 上昇トレンド
    //--- 短期EMAが長期MAの下にある場合
       if(fastma_buffer[0]<slowma_buffer[0])
          trend=-1;  // 下降トレンド
    //---トレンドの方向を返す
       return(trend);
      }
    
    

    トレードアルゴリズムの最後の関数は、新しい足の決定です。 今回のロジックによると、トレンドは新しい足が表示されたときに決定されます。

    //+------------------------------------------------------------------+
    //|現在の時間枠で新しい足の出現をチェックする|
    //| 同時に、トレンドとシグナルを計算 |
    //+------------------------------------------------------------------+
    bool IsNewBar(int &trend)
      {
    //--- 関数呼び出しの間の現在の足の開く時間を永続的に格納。
       static datetime timeopen=0;
    //--- 現在の足のオープン時間を取得 
       datetime time=iTime(NULL, InpMATF, 0);
    //--- 時間が変わっていない場合は、足が新しいわけではないので、'false' 値で終了します
       if(time==timeopen)
          return(false);
    //--- 足が新しく、このトレンド方向を計算する必要があります
       trend=TrendCalculate();
    //--- トレンド方向を取得できなかった場合は、終了して次の呼び出し中に再試行してください
       if(trend==NO_VALUE)
          return(false);
    //--- すべてのチェックが正常に実行:足が新規でトレンドの方向が取得されました
       timeopen=time; //次の呼び出しの現在の時間のオープン時間を記録
    //---
       return(true);
      }
    
    

    上記のロジックにより、すべてのトレード操作が足全体で1回だけ実行されるように、EA操作を整理することができます。 したがって、テスト結果はティック生成モードに依存しません。

    トレードアルゴリズム全体が OnTick() ハンドラで示されます。

    //+------------------------------------------------------------------+
    //| エキスパートティック関数                                             |
    //+------------------------------------------------------------------+
    void OnTick()
      {
       static bool order_sent    =false;    // >現在の足にリミットのオーダーを付けることができませんでした
       static bool order_deleted =false;    // 現在の足のリミットオーダーを削除できませんでした
       static bool order_modified=false;    // 現在の足のリミットオーダーを変更できませんでした
    //--- インプットパラメータが無効な場合は、最初のティックでテストを中止
       if(!ExtInputsValidated)
          TesterStop();
    //---新しい足の出現とトレンドの方向をチェック
       if(IsNewBar(ExtTrend))
         {
          //--- 静的変数の値を元の状態にリセット
          order_sent    =false;
          order_deleted =false;
          order_modified=false;
         }
    //--- 現在の足でチェックコールを1回だけ行う補助変数を作成。
       bool order_exist   =OrderExist();
       bool trend_detected=TrendDetected(ExtTrend);
    //---トレンドがない場合、またはポジションがオープンしている場合は、予約オーダーを削除する
       if(!trend_detected || PositionExist())
          if(!order_deleted)
            {
             order_deleted=DeleteLimitOrders();
             //---オーダーが正常に削除された場合、この足で他の操作は必要ありません
             if(order_deleted)
               {
                //---オーダーの修正を禁止する
                order_sent    =true;
                order_modified=true;
                return;
               }
            }
    
    //---トレンドがある
       if(trend_detected)
         {
          //---オーダーが見つからない場合は、チャネルの境界線にオーダーを配置
          if(!order_exist && !order_sent)
            {
             order_sent=SendLimitOrder(ExtTrend);
             if(order_sent)
                order_modified=true;
            }
          //--- 現在の足で移動されていない場合は、チャネルの境界線にオーダーを移動
          if(order_exist && !order_modified)
             order_modified=ModifyLimitOrder(ExtTrend);
         }
    //---
      }
    
    

    EAの他のトレード関数は標準です。 このソースコードは、MetaTrader5ターミナル標準パッケージで入手できます。 MQL5\Experts\Examplesにあります。

    ナビゲータでのMeanReversionプロジェクトの場所


    パラメータの最適化とセットファイルの追加

    EAの準備が整ったので、ストラテジーテスターで最適なパラメータを見つけましょう。 テストの結果、Ctr+C の組み合わせを使用して、"Settings" タブと "Inputs" タブから値をクリップボードにコピーするオプションが用意されています。 そのため、設定を設定ファイルに保存することなく、フリーランスチャットを介して顧客に設定を提供することができます。 カスタマーは、クリップボードにデータをコピーし、Ctr+Vを使用してテスターの[設定]タブに貼り付けることができます。

    セットファイルへの保存も便利なソリューションです。 マーケットの多くの売り手は、このようなファイルを提供し、プロダクト購買者は即座に適切なパラメータのセットをロードし、必要なツールでEAをテストまたは最適化することができます。 トレードされた各商品に対して、個別のセットファイルを作成する必要があります。 プラットフォームに多くのEAが存在する場合、コンピュータ上のそのようなファイルの数は多くなる可能性があります。 プロジェクトを使用すると、シンボルが変更されるたびにディスク上で検索しなくても、必要なファイルに即座にアクセスできます。

    これが、EX5ファイルに適切なパラメータセットを追加するプロジェクトの例です。 最適化を実行するシンボルを選択します。 たとえば、EURUSDです。 最適化するパラメータの [開始]、[ステップ]、[ストップ] を設定し、最適化プロセスを起動します。 終わったら、[最適化]タブで最適なパスをダブルクリックすると、このパスからのインプットパラメータの値が[パラメータ]タブに挿入され、1つのテストが実行されます。 見つかったパラメータは、セットファイルに保存できます。 ただし、別途提供する必要はありません。 パラメータのセットを、EURUSD.set などの明確な名前で保存します。 パラメータが GBPJPY ではなく、このペアに適用されることを意味します。

    セットファイルへのインプットの保存

    EAがトレードできる各シンボルに対してこの操作を繰り返します。 したがって、準備ができているセットファイルの数が9あるとします。 ファイルをプロジェクトに追加します。 ソース ファイルから分離する適切なフォルダ "Settings and files\Set"を作成します。 プロジェクトを使用すると、オーダーと正しいファイル構造を維持できます。

    プロジェクトへのセットファイルの追加


    次に、プロジェクトをコンパイルし、MeanReversionEAを使用してストラテジーテスターを開きます。 コンテキスト メニューの [インプット] タブに、新しい項目 "EAから読み込み" が表示されます。 使用可能なすべてのセット・ファイルにこのメニューからアクセスできます。

    EAからのインプットパラメータのロード

    したがって、EAのコンパイル済みEX5ファイルは、パラメータの準備ができているセットを持つ完成したプロダクトです。 この戦略は、目的のシンボルのそれぞれに境界線とステップを設定することなく、即座にテストすることができます。 トレードロボットのユーザーと購入者は間違いなくこの利便性を理解するでしょう。


    実際のデータで実行される戦略

    2019年9月、デモアカウントでMeanReversionエキスパートアドバイザーが立ち上げられました。 この目的は、リアルタイムでプログラミングと取引のエラーを見つけることでした。 このEAは、複数のシンボルでポートフォリオモードで起動されました(最適化中の最初の考案されたものです)。 ビルトインVPS は、監視目的で多くの平均レバージョン最適化されたプライベートシグナルが作成されたEA用にレンタルしました。 

    9ヶ月間のトレード結果

    稼働後の最初の月は、このEAはプラスの結果を示しました。 その後、連続5ヶ月の負けが続きました。 仮想ホスティングは自動更新機能でレンタルされ、EAは完全自律モードで動作していました。 その後も資産減少に向けてトレードが続きました。 そして3月には、外国為替相場で何かが変わり、EAは突然、記録的な利益を生み出しました。 次の2ヶ月間、結果は逆になりました。 同じ成長はおそらく2度と繰り返されることはありません。

    銘柄別のトレードと結果の分析によると、損失は3円ペアとAUDUSDによって行われました。 このEAは印象的な結果を示しませんでした。 にもかかわらず、このようなシンプルなトレードロジックを使用しても、ポートフォリオモードで9ヶ月間実行されており、一部のシンボルの損失は他のペアの利益によってカバーされています。

    シンボルによる分布

    EAのパラメータは起動以来変更されたことがなく、この間に追加の移行は行われませんでした。 このEAは9ヶ月前にコンパイルされ、ビルトインVPSで8つのチャートで立ち上げられました。 まだ人間の干渉なしに実行されています。 なぜ9セットファイルのうち8つだけが起動されたのかさえ思い出せません。 また、使用するパラメータも覚えていません。 でも、教育目的で作成された MeanReversionEAプロジェクトは、現在も実行されており、2020年6月10日現在でもプラスの利益を示しています。


    プロジェクトに切り替えて、メリットを享受

    プロジェクトを使用すると、開発者は複雑性の高いプログラムを作成したり、開発中に共同タスクを行うことができます。 同じ志を持つ人々と協力することで、アプリケーションを迅速に開発し、有用なアイデアやスキルを交換し、コードの品質を向上させることができます。

    このEA内で使用するトレードルールは簡単ですが、他の多くのトレーディングロボットを作成するためのテンプレートとして使用することができます。 トレンドの方向、レンジな状態、またはエントリーのレベルとメソッドを決定する関数を置き換えます(たとえば、リミットオーダーではなく成行注文を使用できます)。 おそらく、レンジ期間中にのみトレードする場合は、より良い結果が得られるかもしれません。 また、EAにはトレーリングストップ、ストップロスおよびテイクプロフィットの設定がありません。 EAを改善するためにできることはたくさんあります。

    MetaTrader5標準パッケージのMeanReversionEAを使用して、プロジェクトの利点を研究し評価することができます。 独自のプロジェクトを作成するか、新しいフォルダにコピーして、実験を開始しましょう。 プロジェクトを使用して開始し、利便性を評価してください!