「コナーズの短期売買実践」のトレード戦略のEA化【第1弾】Part2
前回の記事で書いた仕掛け及び手仕舞い条件では、ロジックをプログラム化し難いことがプログラミング中に分かったため、以下の条件に変更しました。
仕掛け条件(ショート専用)
- 1本前のローソク足の高値が、過去60本(日足の場合、過去3ヶ月に相当)の高値を更新
- 1本前のローソク足の出来高が、過去15本の平均出来高の2倍以上
- 1本前のローソク足、今の足、次の足のいずれかで終値が、始値より下に位置している
- 上記条件が全て成立した場合、次のローソク足の始値でショートエントリー
手仕舞い条件
- トレイリングストップ(利食い)
- エントリー時のローソク足の1本前のローソク足の高値に最初のSLを置く(損切り)
「変更しちゃっていいの?」という声が聞こえてきそうですが、私は良いと思っています。まずはざっくりと作ってテストしてみて、脈がありそうだと感じたら、その時に改めて真剣にロジックの設計を見直します。100%の完成度を実現するのが3日必要で、80%の完成度なら1日で良いなら、まずは後者を選ぶわけです。モノづくりってそういうもんです。大体どんなものも試作機から作りますでしょ?
話が逸れました。では、早速動かしてみましょう。バックテストは以下の条件で実施しました。
- 対象EA:DoubleVolumeMarketTop
- 通貨ペア:EURUSD
- ローソク足の種類:日足
- モデル:全ティック
- スプレッド:3pips固定
- バックテスト期間:2008/1/1~2016/7/5
- ヒストリカルデータ:Dukascopy社のJForexからダウンロードした1分足
- 初期口座残高:10,000ドル
- ロット数:0.01固定
結果はこちらです。


元々この手法は日足で紹介されていたので、日足で試してみたのですが、なんと約8年で1回しか条件成立していないではないですか!
試しに1時間足とかで試してみたら、それなりにエントリーがありましたし、条件通り動作していましたので、プログラムは問題ないようです(ちなみに、見事な右肩下がりでしたが)。
見るべきポイントはオレンジ色の枠で囲っていますので、そこを重点的にご注目いただければ幸いです。上記の仕掛け条件と手仕舞い条件をきちんと満たしていることが分かると思います。
このトレード戦略は大量の通貨ペアを同時にウォッチして、条件成立の確率を上げないと使えなそうですね。
それと、これは初めから分かっていたことですが、MT4で取得できる出来高はいわゆる取引量ではなくティック数なので、コナーズ氏の想定していたものとは違います。これも上手く機能しない原因の1つでしょう。
「じゃあ何でこんなものを作ってみようと思ったのか?」ですって?それは、あれです。出来高のVolume()を一度使ってみたかったんです(笑)
DoubleVolumeMarketTop.mq4のコードを公開しますので、良かったら参考になさってください。「1本前のローソク足、今の足、次の足のいずれかで」という部分は他のEAを作成する際に役に立ちそうな気がします。
DoubleVolumeMarketTop.mq4
006 | #property copyright "Copyright (c) 2016, りゅーき" |
007 | #property link "https://autofx100.com/" |
008 | #property version "1.00" |
013 | #include <stderror.mqh> |
015 | #include <WinUser32.mqh> |
016 | #include <Original/Application.mqh> |
017 | #include <Original/Basic.mqh> |
018 | #include <Original/DateAndTime.mqh> |
019 | #include <Original/LotSizing.mqh> |
020 | #include <Original/OrderHandle.mqh> |
021 | #include <Original/OrderReliable.mqh> |
026 | extern string Note01 = "=== General ==================================================" ; |
027 | extern int MagicNumber = 7777777; |
028 | extern int SlippagePips = 5; |
029 | extern string Comments = "" ; |
030 | extern double FixLotSize = 0.01; |
032 | extern string Note02 = "=== Entry ====================================================" ; |
033 | extern int LongPeriod = 60; |
034 | extern int ShortPeriod = 15; |
035 | extern double Times = 2.0; |
037 | extern string Note03 = "=== Exit =====================================================" ; |
038 | extern string Note03_1 = "--- Trailing Stop ---------------------------------------------" ; |
039 | extern double TS_StartPips = 15.0; |
040 | extern double TS_StopPips = 10.0; |
046 | double gPipsPoint = 0.0; |
048 | color gArrowColor[6] = {Blue, Red, Blue, Red, Blue, Red}; |
058 | gPipsPoint = currencyUnitPerPips(Symbol()); |
059 | gSlippage = getSlippage(Symbol(), SlippagePips); |
063 | return (INIT_SUCCEEDED); |
069 | void OnDeinit( const int reason) |
078 | int currentBars = Bars; |
083 | trailingStopGeneral(MagicNumber, TS_StartPips, TS_StopPips); |
089 | if (currentBars == gPrvBars){ |
090 | gPrvBars = currentBars; |
098 | double latestHigh = NormalizeDouble(High[1], Digits); |
099 | double longTermHigh = NormalizeDouble(High[iHighest(NULL, 0, MODE_HIGH, LongPeriod, 2)], Digits); |
103 | if (latestHigh >= longTermHigh){ |
108 | double avgVolume = 0.0; |
110 | for ( int i = 2; i <= ShortPeriod + 1; i++){ |
111 | avgVolume += Volume[i]; |
114 | avgVolume = avgVolume / ( double )ShortPeriod; |
118 | if (Volume[1] >= avgVolume * Times){ |
122 | if (gBars == 0 && flg1 && flg2){ |
129 | if (currentBars <= gBars + 2){ |
130 | if (Close[1] < Open[1]){ |
132 | double sl = NormalizeDouble(High[1], Digits); |
141 | int ticket = orderSendReliable(Symbol(), OP_SELL, FixLotSize, Bid, gSlippage, sl, 0, Comments, MagicNumber, 0, gArrowColor[OP_SELL]); |
145 | gPrvBars = currentBars; |