【MT5】MQLウィザードで作成した移動平均EAを最適化してみる

MQLウィザードで作成した移動平均EAを最適化してみる メタトレーダー5

前回MQLウィザードを使って移動平均を利用したプログラムをちゃちゃっと作ってみました。今回は作成したプログラムを利用して最適化をやってみます。

勘に頼るのではなく、実際にパラメータを無数に動かして最適な結果が出せる組み合わせが見つけられます。

そう、MT5ならね。

最適化の進め方とともにどれぐらいパフォーマンスが上がるのかもお見せします。

前回はCFDでバックテストをしましたが、今回はFX(ドル円)をバックテストの対象としています。

ちなみにメタトレーダー5では「最適化」は「オプティマイズ」と表現されています。あまりピンとこないので独断と偏見により本記事ではUIとして記載する以外は最適化と記載します。

この記事で分かること
  • MQL5ウィザードを使って作成したプログラムのパラメータの内容
  • 最適化の実施方法
  • 最適化されたパラメータをプログラムに組み込む方法

使用するプログラム

前回の記事で作った移動平均を利用したプログラム(調整後)を使用します。

作成の仕方は以下記事をご覧ください。

ソースコードも張っておきます。

//+------------------------------------------------------------------+
//|                                                        test1.mq5 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include                                                          |
//+------------------------------------------------------------------+
#include <Expert\Expert.mqh>
//--- available signals
#include <Expert\Signal\SignalMA.mqh>
//--- available trailing
#include <Expert\Trailing\TrailingNone.mqh>
//--- available money management
#include <Expert\Money\MoneyFixedLot.mqh>
//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
//--- inputs for expert
input string             Expert_Title         ="test1";     // Document name
ulong                    Expert_MagicNumber   =19169;       //
bool                     Expert_EveryTick     =false;       //
//--- inputs for main signal
input int                Signal_ThresholdOpen =10;          // Signal threshold value to open [0...100]
input int                Signal_ThresholdClose=10;          // Signal threshold value to close [0...100]
input double             Signal_PriceLevel    =0.0;         // Price level to execute a deal
input double             Signal_StopLevel     =50.0;        // Stop Loss level (in points)
input double             Signal_TakeLevel     =50.0;        // Take Profit level (in points)
input int                Signal_Expiration    =4;           // Expiration of pending orders (in bars)
input int                Signal_MA_PeriodMA   =12;          // Moving Average(12,0,...) Period of averaging
input int                Signal_MA_Shift      =0;           // Moving Average(12,0,...) Time shift
input ENUM_MA_METHOD     Signal_MA_Method     =MODE_SMA;    // Moving Average(12,0,...) Method of averaging
input ENUM_APPLIED_PRICE Signal_MA_Applied    =PRICE_CLOSE; // Moving Average(12,0,...) Prices series
input double             Signal_MA_Weight     =1.0;         // Moving Average(12,0,...) Weight [0...1.0]
//--- inputs for money
input double             Money_FixLot_Percent =10.0;        // Percent
input double             Money_FixLot_Lots    =0.1;         // Fixed volume
//+------------------------------------------------------------------+
//| Global expert object                                             |
//+------------------------------------------------------------------+
CExpert ExtExpert;
//+------------------------------------------------------------------+
//| Initialization function of the expert                            |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Initializing expert
   if(!ExtExpert.Init(Symbol(),Period(),Expert_EveryTick,Expert_MagicNumber))
     {
      //--- failed
      printf(__FUNCTION__+": error initializing expert");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Creating signal
   CExpertSignal *signal=new CExpertSignal;
   if(signal==NULL)
     {
      //--- failed
      printf(__FUNCTION__+": error creating signal");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//---
   ExtExpert.InitSignal(signal);
   signal.ThresholdOpen(Signal_ThresholdOpen);
   signal.ThresholdClose(Signal_ThresholdClose);
   signal.PriceLevel(Signal_PriceLevel);
   signal.StopLevel(Signal_StopLevel);
   signal.TakeLevel(Signal_TakeLevel);
   signal.Expiration(Signal_Expiration);
//--- Creating filter CSignalMA
   CSignalMA *filter0=new CSignalMA;
   if(filter0==NULL)
     {
      //--- failed
      printf(__FUNCTION__+": error creating filter0");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
   signal.AddFilter(filter0);
//--- Set filter parameters
   filter0.PeriodMA(Signal_MA_PeriodMA);
   filter0.Shift(Signal_MA_Shift);
   filter0.Method(Signal_MA_Method);
   filter0.Applied(Signal_MA_Applied);
   filter0.Weight(Signal_MA_Weight);
   filter0.Pattern_0(0);
//--- Creation of trailing object
   CTrailingNone *trailing=new CTrailingNone;
   if(trailing==NULL)
     {
      //--- failed
      printf(__FUNCTION__+": error creating trailing");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Add trailing to expert (will be deleted automatically))
   if(!ExtExpert.InitTrailing(trailing))
     {
      //--- failed
      printf(__FUNCTION__+": error initializing trailing");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Set trailing parameters
//--- Creation of money object
   CMoneyFixedLot *money=new CMoneyFixedLot;
   if(money==NULL)
     {
      //--- failed
      printf(__FUNCTION__+": error creating money");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Add money to expert (will be deleted automatically))
   if(!ExtExpert.InitMoney(money))
     {
      //--- failed
      printf(__FUNCTION__+": error initializing money");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Set money parameters
   money.Percent(Money_FixLot_Percent);
   money.Lots(Money_FixLot_Lots);
//--- Check all trading objects parameters
   if(!ExtExpert.ValidationSettings())
     {
      //--- failed
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Tuning of all necessary indicators
   if(!ExtExpert.InitIndicators())
     {
      //--- failed
      printf(__FUNCTION__+": error initializing indicators");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- ok
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Deinitialization function of the expert                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ExtExpert.Deinit();
  }
//+------------------------------------------------------------------+
//| "Tick" event handler function                                    |
//+------------------------------------------------------------------+
void OnTick()
  {
   ExtExpert.OnTick();
  }
//+------------------------------------------------------------------+
//| "Trade" event handler function                                   |
//+------------------------------------------------------------------+
void OnTrade()
  {
   ExtExpert.OnTrade();
  }
//+------------------------------------------------------------------+
//| "Timer" event handler function                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
   ExtExpert.OnTimer();
  }
//+------------------------------------------------------------------+

初期状態のパラメータでのテスト結果

2019年のドル円1時間足チャートに対して自動売買プログラムを走らせて、最適化するようにパラメータを決めていきます。

まずは最適化を施す前(プログラムを作成時の初期状態)でのテスト結果がどうなるかを見ておきましょう。

バックテストを実施した結果、1,000,000円の資金が999,250円に減りました。プロフィットファクターは0.86です。

プロフィットファクターとは、システムトレードでのパフォーマンスの指標の1つで、期間内の総利益が総損失の何倍か(利益÷総損失)で計算されます。
数値が大きいほどパフォーマンスがよいことを表します。

最適化前テスト結果(グラフタブ)
最適化前テスト結果(グラフタブ)[クリックで拡大]
最適化前テスト結果(バックテストタブ)
最適化前テスト結果(バックテストタブ)[クリックで拡大]
クノウ
クノウ

うわっ、空気読めずにまあまのパフォーマンス出しおった!

パラメータの内容を理解する

気を取り直していきましょう。我々が求めているのはプラスのパフォーマンスなのですから。

設定できるすべてのパラメータは、ストラテジーテスターのパラメータタブで確認できます。このパラメータを変更しながらテストを繰り返すことで一番良い結果を得られるパラメータの組み合わせを見つけていきます。

まずは、それらが何のためのパラメータなのかを理解するところから始めなければなりません。

パラメータ
パラメータ[クリックで拡大]

至極シンプルなプログラムにしたにも関わらすパラメータは14個あります。

  • Document name
  • Signal threshold value to open [0…100]
  • Signal threshold value to close [0…100]
  • Price level to execute a deal
  • Stop Loss level (in points)
  • Take Profit level (in points)
  • Expiration of pending orders (in bars)
  • Moving Average(12,0,…) Period of averaging
  • Moving Average(12,0,…) Time shift
  • Moving Average(12,0,…) Method of averaging
  • Moving Average(12,0,…) Prices series
  • Moving Average(12,0,…) Weight [0…1.0]
  • Percent
  • Fixed volume

以下の3分類でマーカをつけています。それぞれの分類に分けてパラメータを一つ一つ見ていきます。最適化するのに最適かどうかの観点で、仕分け(採用/不採用)も行います

  • 共通パラメータ
  • 移動平均指標用パラメータ
  • 固定ロットでの取引用パラメータ

共通パラメータ

共通パラメータは、MQLウィザードを使って作成したプログラムに共通で設定されるパラメータです。

Document name

プログラムのファイル名を指定します。ファイル名を変えられるようです。パラメータにする必要あるのか…?

当たり前ですが、最適化できないので本パラメータは不採用です。

Signal threshold value to open [0…100]

0~100の間でポジションオープン(新規注文)用のしきい値を指定します。

今回のプログラムは移動平均の指標のみを使った単純なものなので固定とし、本パラメータは不採用とします。

Signal threshold value to close [0…100]

0~100でポジションクローズ(決済注文)用のしきい値を指定します。

オープンしきい値と同様の理由で本パラメータは不採用とします。

Price level to execute a deal

新規注文を出す際の注文を置く位置を指定します。

  • 0:成行注文
  • プラスの値:指定したポイントを離して指値注文をするように設定
  • マイナスの値:指定したポイントを離して逆指値注文をするように設定

だましの回避などで有効かもしれません。本パラメータは採用です。

Stop Loss level (in points)

新規注文の際の損切り(ストップロス)を指定します。

  • 0:損切りなし
  • プラスの値:注文価格から指定したポイントを離して損切りを設定

最適化の肝となる部分ですので本パラメータは採用します。

Take Profit level (in points)

新規注文の際の利食い(テイクプロフィット)を指定します。

  • 0:利食いなし
  • プラスの値:注文価格から指定したポイントを離して利食いを設定

損切りと共に最適化の肝となる部分ですので本パラメータは採用です。

Expiration of pending orders (in bars)

新規注文(指値/逆指値)がキャンセルされる期間を指定します。

  • 0:利食いなし
  • プラスの値:注文を出して何本目の足に達するとキャンセルするかを設定

Price level to execute a dealを採用したので本パラメータもバーターとして採用です。

Price level to execute a dealが0の場合は設定しても意味なしです。

移動平均指標用パラメータ

共通移動平均指標用パラメータは、売買シグナルとして「Signals of indicator ‘Moving Average’」(移動平均指標)を選択した場合に設定されるパラメータです。

各指標用のパラメータの簡単な説明は、MQL5 リファレンスから確認できます。(確認の仕方はこちら

Moving Average(12,0,…) Period of averaging

移動平均指標の平均化の期間を指定します。

  • プラスの値:指定した足数で平均値を算出するように設定

期間によって成績が大きく変わりますので本パラメータは採用です。

Moving Average(12,0,…) Time shift

移動平均指標をずらす(シフトする)幅を指定します。

  • 0:シフトなし
  • プラスの値:指定した足数だけ前(未来)にずれるように設定
  • マイナスの値:指定した足数だけ後ろ(過去)にずれるように設定

メジャーな手法なので本パラメータは採用です。

移動平均を前または後ろにずらしたものはDMA(Displaced Moving Average)と呼ばれます。

Moving Average(12,0,…) Method of averaging

移動平均指標の平均化手法を4種類から指定します。それぞれの移動平均についての詳細はこちらをご覧ください。

  • Simple:単純移動平均を設定
  • Exponential:指数移動平均を設定
  • Smoothed:平滑移動平均を設定
  • Linear weighted:線形加重移動平均を設定

根本的な計算方法が違ってきますので本パラメータは採用です。

Moving Average(12,0,…) Prices series

移動平均指標を算出する価格を7種類から指定します。

  • Close price:終値を設定
  • Open price:始値を設定
  • High price:高値を設定
  • Low price:安値を設定
  • Median price:中間値((高値+安値)/2)を設定
  • Typical price:典型値((高値+安値+終値)/3)を設定
  • Weighted price:加重終値((高+安+終+終)/4)を設定

結構な数を採用しているので終値で固定とします。本パラメータは不採用です。

Moving Average(12,0,…) Weight [0…1.0]

0~1.0の間で移動平均指標の重みを指定します。

今回は単一指標のみを組み込んでいるため1.0に固定とします。本パラメータは不採用です。

固定ロットでの取引用パラメータ

固定ロットでの取引用パラメータは、プログラム作成時に資金管理プロパティとして「Trading with fixed trade volume」(固定ロットで取引)を選択した場合に設定されるパラメータです。

Percent

「数字を変えても結果が変わらない」「ソースコードを確認しても使われているようには見えなかった」ため、固定ロットで取引時には使われないパラメータと予想します。

惑わされましたが、本パラメータは不採用です。

Fixed volume

取引をする際のロット数を指定します。

0.1ロット(10,000通貨)で固定とするため、本パラメータは不採用です。

採用したパラメータの範囲を決める

採用したパラメータをまとめて、テストをする範囲を決めます。

最適化を行うパラメータには開始値と終了値、ステップ(増分)を指定します。

パラメータ開始値ステップ終了値
Price level to execute a deal-501050
Stop Loss level (in points)010100
Take Profit level (in points)010100
Expiration of pending orders (in bars)015
Moving Average(12,0,…) Period of averaging5525
Moving Average(12,0,…) Time shift-313
Moving Average(12,0,…) Method of averagingSimpleLinear weighted

1ポイント=1銭です。100ポイントの損切りは1円逆に動いたら損切りとなります。

最適化を実行する

では準備が整いましたのでいよいよ最適化を行います。

ステップ1
ストラテジーテスターで設定タブをクリック → テスト対象などを選択

最適化 ステップ1
ステップ1[クリックで拡大]
  1. エキスパート:
    プログラムを選択します。
  2. 銘柄:
    通貨、足を選択します。
  3. 日付:
    バックテスト対象期間を選択します。
  4. モデル:
    テストに使用するデータの細かさを選択します。

    今回はティックに絡めて取引を決定するプログラムではないですが利食い・損切りで関係してくるため、全ティックを選択しています。より実際に近づけたい場合は、さらに細かいデータであるリアルティックに基づいた全てのティックを選んだ方がよさそうです。(モデルによる違いの詳細はこちら

  5. 入金:
    テスト開始時の資金を入力します。
  6. レバレッジ:
    レバレッジを選択します。FXなので25倍としました。
  7. オプティマイズ:
    最適化の方法・対象を選択します。

    遺伝的アルゴリズム(速い)は、完全アルゴリズム(遅い)とほぼ同じ質で実行時間をはるかに短くする方法です。(最適化の種類の詳細はこちら
    遺伝的アルゴリズム(速い)でのバックテストは実行するたびに結果が少し変わります(なぜかは分かりません)。


ステップ2
パラメータタブをクリック → 最適化をするパラメータにチェックを入れてスタート、ステップ、ストップを入力

最適化 ステップ2
ステップ2[クリックで拡大]

ステップ3
右下にあるスタートをクリック

 ➡テストが開始されます。

私の環境では結果が出るまで30分強かかりました。

結果発表

オプティマイズ結果タブにテスト結果が表示されます。(結果の見方の詳細はこちら

パラメータの設定によっては1,000,000円の資金が1,083,661円に増えることがわかります。この時のプロフィットファクターは2.34でした。

最適化によりプロフィットファクターが0.86→2.34に増加しており、172%という素晴らしい増加率となっています。

最適化結果
最適化結果[クリックで拡大]
  • 右クリックメニューからオプティマイズ結果にプロフィットファクタードローダウン %などの項目を列に追加することもできます。
  • 最適化において、2,766通りのパターンをテストしていて、最低のテストケースでは713,420円(-286,580円)となっていました。
    すべてのパターンは1,118,040通りなので遺伝的アルゴリズムの有能さと完全アルゴリズム(遅い)で最適化したときの途方もなさに震えます。

パラメータの最適化はあくまで最適化した期間でのパフォーマンスがよくなる組み合わせが得られるだけです。一番よかった組み合わせをそのまま運用するのが良い訳ではありません。

バックテストで詳細を確認する

気になる結果の上で右クリック → この設定でバックテストをクリックするとバックテストが実行され、最適化されたパラメータでの詳細なバックテスト結果が確認できます。

バックテストの実行
バックテストの実行
テスト結果(グラフタブ)
最適化後テスト結果(グラフタブ)[クリックで拡大]
テスト結果(バックテストタブ)
最適化後テスト結果(バックテストタブ)[クリックで拡大]
クノウ
クノウ

このEA買いたくないですか?(中身は移動平均)

最適なパラメータをプログラムに組み込む

「これだっ!」というパラメータが見つかりましたら最後の仕上げです。プログラムに組み込まれているパラメータを更新します。

メタエディターでプログラムを開き、[Inputs]部分のパラメータを更新し、コンパイルをします。

プログラムの変更
プログラムの変更

Signal_MA_Methodは以下の対応表を参考に書き換えてください。

平均化手法書き方
SimpleMODE_SMA
ExponentialMODE_EMA
SmoothedMODE_SMMA
Linear weightedMODE_LWMA

あとがき

今回はMQLウィザードを利用すると数分もあれば作成できるプログラムが最適化を施すことによりパフォーマンスがどれほど上がるのかを見てもらいつつ、最適化のやり方についてお伝えしました。

ある程度の期間で良いパフォーマンスを出せるEAは、簡単に最適化によって生み出すことができるというのを感じてもらえたのではないでしょうか。

パラメータを数多く用意すれば、過去の相場において利益が出るパターンも見つけられることでしょう。かといってそのパラメータが実運用でいいパフォーマンスと出すことに直結するわけではありません。

最適化しすぎることによる「最適化の罠(カーブフィッティング)」、奥が深いです。。

コメント

タイトルとURLをコピーしました