EA
インストールが終わったら、実際にEAを動かしてみましょう。
既存のEAを動かしてみる
早速、プリセットのEAでバックテストを行ってみましょう。 過去の価格情報さえあれば、非常に簡単にテストできるのがMT5の魅力です。
左下のウィンドウに「ナビゲータ」というものがありますが、その中の「エキスパートアドバイザ(EA)」>「Examples」>「Moving Average」を右クリックし、「テスト」をクリックしてください。 すると、下のウィンドウにテスト用の設定が出てきます。
設定項目は、ここでは「EURUSD」の1時間足(H1)、期間指定(2015/01/01 - 2015/12/31)としてみます。 何か好きな通貨や期間があれば、それを選んでも問題ありません。 設定が終わったら、「スタート」を押してみます。
謎のコミカルな効果音がしたら、テスト完了です。 マシンスペックに依りますが、軽いEAならものの数秒で1年分テストできます。 「結果」タブを見てみましょう。
グラフもあり、かなり充実しています。
さて、実際テスト期間でどれだけ損失が出たかは、左上の「総損益」を見るとわかります。 どうやら-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」ボタンを押して、エラーが出なければ、晴れて完成です。 実際に同じ条件でテストを走らせてみましょう。
悲しいことに、損失が元より増えていますね(-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刻みで変動するようにしてみましょう。
チェックボックスにチェックが入っていることを確認し、「設定」タブから「オプティマイズ」を「完全アルゴリズム(遅い)」、「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)