EA

インストールが終わったら、実際にEAを動かしてみましょう。

既存のEAを動かしてみる

早速、プリセットのEAでバックテストを行ってみましょう。 過去の価格情報さえあれば、非常に簡単にテストできるのがMT5の魅力です。

左下のウィンドウに「ナビゲータ」というものがありますが、その中の「エキスパートアドバイザ(EA)」>「Examples」>「Moving Average」を右クリックし、「テスト」をクリックしてください。 すると、下のウィンドウにテスト用の設定が出てきます。

moving_average

設定項目は、ここでは「EURUSD」の1時間足(H1)、期間指定(2015/01/01 - 2015/12/31)としてみます。 何か好きな通貨や期間があれば、それを選んでも問題ありません。 設定が終わったら、「スタート」を押してみます。

謎のコミカルな効果音がしたら、テスト完了です。 マシンスペックに依りますが、軽いEAならものの数秒で1年分テストできます。 「結果」タブを見てみましょう。

result

グラフもあり、かなり充実しています。

さて、実際テスト期間でどれだけ損失が出たかは、左上の「総損益」を見るとわかります。 どうやら-1869.07と、資産の20%近くをふっ飛ばした模様です。 これはいけませんね。

わかりやすい所で、トレードの勝率を見てみましょう。 真ん中右辺りでは、取引の詳細が書かれています。 勝率は29%、負率は70%の模様です。 また、最大勝ちトレードでは+378.17を出していますが、最大負けトレードでは-540.17となっています。

期間特有の動きも見てみましょう。 いい分析にはなりにくいと思われますが、アノマリーが見つかるかもしれません。 5~6時の取引で青いバー(利益)が飛び出しており、16~23時では赤いバー(損失)が多く飛び出しています。 また、月・水に弱かったり、1~4月、9月に多く損失を出していたりします。

EAの調整

上記を踏まえて、実際にEAのスクリプトを変更してみましょう。 下記3種類の修正を独立して適用し、結果がどう変わるかを見てみます。

  • 損切りを早くする(最大負けを減らす)
  • 好条件の時のみポジションを取る(勝率を増やす)
  • 5~6時、16~23時、月、水、1~4月、9月は取引をしない(期間特有の調整。バックテストマジック)

準備

既存のEAは再度実行したいため、コピーを取り、それを変更しましょう。 右クリックしても「エクスプローラーで開く」というメニューが無いため、インストールしたディレクトリを潜るか、後述するMeta Editorで開いてからコピーしましょう。

Meta Editor

MT5には、Meta EditorなるIDEが付属しています。 決して強力なエディターとは言えないですが、最低限必要な機能はあるかな……と言った感じです。 ひとまずはこれを使うことにします。

MT5の「Moving Average」を右クリックし、「変更」をクリックすると、Meta Editorが現れます。 左のペイン「Navigator」から「Experts」>「Examples」>「Moving Average」>「Moving Average.mq5」が現在開いているファイルです。 ここを右リックすると、「Open Folder」があり、エクスプローラーで直接開くことができます。 まだコピーを取っていなければ、これで開いてコピーすると楽ちんです。

損切りを早くしてみる

では、実際に変更をしてみましょう。 常に損失を確認し、一定以上になったらクローズする、という方法もありますが、今回は簡単に「SL(ストップロス)価格を設定する」ことにします。

SLはポジションを開く際に同時に設定できますので、そのあたりのコードを変更すれば良さそうです。 コードを眺めてみると、117行目付近でポジションを開いていることがわかります。 では、以下のように書き換えてみましょう。

@@ -116,6 +116,14 @@
      {
-      if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && Bars(_Symbol,_Period)>100)
-         ExtTrade.PositionOpen(_Symbol,signal,TradeSizeOptimized(),
-                               SymbolInfoDouble(_Symbol,signal==ORDER_TYPE_SELL ? SYMBOL_BID:SYMBOL_ASK),
-                               0,0);
+      if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && Bars(_Symbol,_Period)>100) {
+         double volume = TradeSizeOptimized();
+         double price = SymbolInfoDouble(_Symbol, signal==ORDER_TYPE_SELL ? SYMBOL_BID : SYMBOL_ASK);
+         
+         const double MAX_LOSS = 100.0;
+         const double LOT_SIZE = 100000;
+         double sl_diff = MAX_LOSS / volume / LOT_SIZE;
+         double sl = (signal==ORDER_TYPE_SELL ? price + sl_diff : price - sl_diff);
+         Print(price);
+         Print(sl);
+         ExtTrade.PositionOpen(_Symbol, signal, volume, price, sl, 0);
+      }
      }

1ロットが10万通貨(海外では一般的)の設定ですが、お使いのブローカーに合わせて変更してください。

上記コードで何をしているかというと、100.0の損失を出す価格を求め、それをSLとして引数に渡しているだけです。 元のコードと比べると、計算用に引数を変数に括りだしているため、多く変更されているように見えますが、やっていることはそれだけです。

さて、「Compile」ボタンを押して、エラーが出なければ、晴れて完成です。 実際に同じ条件でテストを走らせてみましょう。

Moving Average SL version result

悲しいことに、損失が元より増えていますね(-1869.07 => -2153.67)。 最大負けトレードは-100.44と、ちゃんとSLは効いているようです。 取引数が17回増え(289 => 306)、負けトレードがその分増えています。 これが損失増加の原因となっている模様です。

SLを掛けるとポジションが解消されやすくなるため、今回のスクリプトの作りでは取引回数が増えてしまいます。 今回テストした期間では、余計なエントリーが増えてしまい、損失に繋がってしまいました。

パラメーターの調整

では、200.0まで損失を許容するとどうなるでしょうか? 書き換えて、再度実行してみましょう。

今度は50ほど損失が減少しました(-1869.07 => -1810.89)。 余計なエントリーが減った模様です。

当然、このような小手先の調整をした所で、テスト期間が変われば役に立ちません。 が、様々なパラメーターを試すことで、多くの期間で通用する値や、特定の状況で高い効果を発揮する値が見つかるかもしれません。 色々と試して分析することは重要だと考えられます。

さて、パラメーターを書き換えるのにいちいちコンパイルしていては面倒ですね。 MT5では、input型指定子を付けてグローバル変数として宣言することで、「パラメータ」タブから変更することが可能になります。 さらに、何通りものパラメーターを自動的に試し、最も良い結果を探すこともできます。

input int    MovingShift        = 6;       // Moving Average shift
+ input double MAX_LOSS = 200.0;

「パラメータ」タブから、MAX_LOSSが50.0~300.0まで20.0刻みで変動するようにしてみましょう。

optimize

チェックボックスにチェックが入っていることを確認し、「設定」タブから「オプティマイズ」を「完全アルゴリズム(遅い)」、「Balance max」にしてスタートを押してみましょう。

すると、「オプティマイズ結果」というタブができ、それぞれのMAX_LOSSでの結果が表示されています。 ここでは、MAX_LOSS=290の時、-1576.37で一番良い結果になったようです。

このように、パラメーターの最適化を自動で行うことができます。便利!

printfデバッグ

先ほどのコードには、Print関数が書かれていました。 これは、テストの実行中に確認できます。 「操作ログ」タブを見ると、コード中にPrint関数で指定した値が書き出されています。 これにより、いわゆるprintfデバッグが可能になります。

好条件の時のみポジションを取る

さて、では次の条件を実装してみましょう。 先ほど追加した損切りの入っていない、元のコードを変更します。

条件のよい時にのみポジションを取るようにすれば、勝率が上がると考えられます。 今回の移動平均を利用したポジション取りの場合、好条件とはどのような時でしょうか?

様々な条件が挙がると思いますが、順張りで大きな動きを取れると良さそうですので、今回は以下の条件で実装してみます。

  • 移動平均線を上抜いた(下抜いた)際に、すぐポジションは取らない
  • その後続けて上昇(下降)した場合にポジションを取る

この方法だと、大きなトレンドを掴みやすくなると考えられます。 一方、ポジションを取るのが遅くなるので、高値掴みする可能性は上がると思われます。

では早速実装してみましょう。

@@ -20,2 +20,11 @@

+enum PRE_ORDER {
+   PRE_ORDER_NONE,
+   PRE_ORDER_BUY,
+   PRE_ORDER_SELL
+};
+PRE_ORDER pre_order = PRE_ORDER_NONE;
+int pre_order_count = 0;
+input int CONTINUOUS_LENGTH = 2;
+
 #define MA_MAGIC 1234501
@@ -103,10 +112,36 @@
      }
+   
+//--- check pre-order
+   PRE_ORDER target = PRE_ORDER_NONE;
+   if(rt[0].open <= rt[0].close)
+      target = PRE_ORDER_BUY;
+   else if(rt[0].open > rt[0].close)
+      target = PRE_ORDER_SELL;
+   
+   if(pre_order != PRE_ORDER_NONE) {
+      if(pre_order != target) {   // 違っていたら
+         pre_order = PRE_ORDER_NONE;
+         pre_order_count = 0;
+      }
+      else {
+         pre_order_count++;
+      }
+   }
+
 //--- check signals
    ENUM_ORDER_TYPE signal=WRONG_VALUE;
-
-   if(rt[0].open>ma[0] && rt[0].close<ma[0])
-      signal=ORDER_TYPE_SELL;    // sell conditions
-   else
-     {
-      if(rt[0].open<ma[0] && rt[0].close>ma[0])
+   if(rt[0].open>ma[0] && rt[0].close<ma[0]) {
+      if(pre_order == PRE_ORDER_NONE) {
+         pre_order = PRE_ORDER_SELL;
+         pre_order_count = 0;
+      }
+      if(pre_order_count == CONTINUOUS_LENGTH)
+         signal=ORDER_TYPE_SELL;    // sell conditions
+   }
+   else if(rt[0].open<ma[0] && rt[0].close>ma[0]) {
+      if(pre_order == PRE_ORDER_NONE) {
+         pre_order = PRE_ORDER_BUY;
+         pre_order_count = 0;
+      }
+      if(pre_order_count == CONTINUOUS_LENGTH)
          signal=ORDER_TYPE_BUY;  // buy conditions
@@ -116,2 +151,4 @@
      {
+     pre_order = PRE_ORDER_NONE;
+     pre_order_count = 0;
       if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && Bars(_Symbol,_Period)>100)