前回MQLウィザードを使って移動平均を利用したプログラムをちゃちゃっと作ってみました。今回は作成したプログラムを利用して最適化をやってみます。
勘に頼るのではなく、実際にパラメータを無数に動かして最適な結果が出せる組み合わせが見つけられます。
そう、MT5ならね。
最適化の進め方とともにどれぐらいパフォーマンスが上がるのかもお見せします。
前回はCFDでバックテストをしましたが、今回はFX(ドル円)をバックテストの対象としています。
ちなみにメタトレーダー5では「最適化」は「オプティマイズ」と表現されています。あまりピンとこないので独断と偏見により本記事ではUIとして記載する以外は最適化と記載します。
使用するプログラム
前回の記事で作った移動平均を利用したプログラム(調整後)を使用します。
作成の仕方は以下記事をご覧ください。
ソースコードも張っておきます。
//+------------------------------------------------------------------+
//| 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です。
うわっ、空気読めずにまあまのパフォーマンス出しおった!
パラメータの内容を理解する
気を取り直していきましょう。我々が求めているのはプラスのパフォーマンスなのですから。
設定できるすべてのパラメータは、ストラテジーテスターのパラメータタブで確認できます。このパラメータを変更しながらテストを繰り返すことで一番良い結果を得られるパラメータの組み合わせを見つけていきます。
まずは、それらが何のためのパラメータなのかを理解するところから始めなければなりません。
至極シンプルなプログラムにしたにも関わらすパラメータは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を採用したので本パラメータもバーターとして採用です。
移動平均指標用パラメータ
共通移動平均指標用パラメータは、売買シグナルとして「Signals of indicator ‘Moving Average’」(移動平均指標)を選択した場合に設定されるパラメータです。
Moving Average(12,0,…) Period of averaging
移動平均指標の平均化の期間を指定します。
- プラスの値:指定した足数で平均値を算出するように設定
期間によって成績が大きく変わりますので本パラメータは採用です。
Moving Average(12,0,…) Time shift
移動平均指標をずらす(シフトする)幅を指定します。
- 0:シフトなし
- プラスの値:指定した足数だけ前(未来)にずれるように設定
- マイナスの値:指定した足数だけ後ろ(過去)にずれるように設定
メジャーな手法なので本パラメータは採用です。
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 | -50 | 10 | 50 |
Stop Loss level (in points) | 0 | 10 | 100 |
Take Profit level (in points) | 0 | 10 | 100 |
Expiration of pending orders (in bars) | 0 | 1 | 5 |
Moving Average(12,0,…) Period of averaging | 5 | 5 | 25 |
Moving Average(12,0,…) Time shift | -3 | 1 | 3 |
Moving Average(12,0,…) Method of averaging | Simple | Linear weighted |
最適化を実行する
では準備が整いましたのでいよいよ最適化を行います。
ステップ1
ストラテジーテスターで設定タブをクリック → テスト対象などを選択
- ①エキスパート:
プログラムを選択します。 - ②銘柄:
通貨、足を選択します。 - ③日付:
バックテスト対象期間を選択します。 - ④モデル:
テストに使用するデータの細かさを選択します。 - ⑤入金:
テスト開始時の資金を入力します。 - ⑥レバレッジ:
レバレッジを選択します。FXなので25倍としました。 - ⑦オプティマイズ:
最適化の方法・対象を選択します。
ステップ2
パラメータタブをクリック → 最適化をするパラメータにチェックを入れてスタート、ステップ、ストップを入力
ステップ3
右下にあるスタートをクリック
➡テストが開始されます。
結果発表
オプティマイズ結果タブにテスト結果が表示されます。(結果の見方の詳細はこちら)
パラメータの設定によっては1,000,000円の資金が1,083,661円に増えることがわかります。この時のプロフィットファクターは2.34でした。
最適化によりプロフィットファクターが0.86→2.34に増加しており、172%という素晴らしい増加率となっています。
バックテストで詳細を確認する
気になる結果の上で右クリック → この設定でバックテストをクリックするとバックテストが実行され、最適化されたパラメータでの詳細なバックテスト結果が確認できます。
このEA買いたくないですか?(中身は移動平均)
最適なパラメータをプログラムに組み込む
「これだっ!」というパラメータが見つかりましたら最後の仕上げです。プログラムに組み込まれているパラメータを更新します。
メタエディターでプログラムを開き、[Inputs]部分のパラメータを更新し、コンパイルをします。
あとがき
今回はMQLウィザードを利用すると数分もあれば作成できるプログラムが最適化を施すことによりパフォーマンスがどれほど上がるのかを見てもらいつつ、最適化のやり方についてお伝えしました。
ある程度の期間で良いパフォーマンスを出せるEAは、簡単に最適化によって生み出すことができるというのを感じてもらえたのではないでしょうか。
パラメータを数多く用意すれば、過去の相場において利益が出るパターンも見つけられることでしょう。かといってそのパラメータが実運用でいいパフォーマンスと出すことに直結するわけではありません。
最適化しすぎることによる「最適化の罠(カーブフィッティング)」、奥が深いです。。
コメント