どんな記事
この記事を読むと次のことができる(わかる)ようになります。
- 「投資の魔術」掲載の手法をTradingViewで再現
- 誰でも同じバックテストができる
- 10年以上むかしの手法が**〝現在でも通用するのか〟が分かる**
追記・修正
2018年12月5日 修正
「Pineスクリプト」のコードを一部修正しました。(「,style=linebr」を追加)
2018年12月8日 追記
「TradingViewでも公開しています!」を追加しました
タートルズ流 投資の魔術 からの引用
まずは、この記事の主題でもある「ATRチャネル・ブレイクアウト」について書かれている箇所を「伝説のトレーダー集団 タートルズ流投資の魔術」から引用します。
ATRチャネル・ブレイクアウト・システムは、変動性の尺度として真の値幅の平均(ATR)を使用するものだ。チャネルは、350日の終値の移動平均にATRの7倍を加えたものを上限とし、ATRの3倍を引いたものを下限として形成される。前日の終値がチャネルの最高値を超えていたら寄り付きから買い持ちのトレードが仕掛けられ、前日の終値がチャネルの最安値より下回れば、売り持ちとなる。価格が戻して終値が移動平均とクロスしたら、取引は手じまいだ。
ATRチャネル・ブレイクアウト・システムのバリエーションのひとつは、PGO(プリティー・グッド・オシレーター)システムという名称で、チャック・ルボーズ・システムトレーダーズ・クラブ( www.traderclub.com )フォーラムのトレーダー、マーク・ジョンソンが普及させた。これは次に述べるボリンジャー・ブレイクアウト・システムの変形でもある。
この記事は、上記の手法をTradingViewで再現することを目的としたものです。
ストラテジーを作成
チャートが再現できているか確認するために、「伝説のトレーダー集団 タートルズ流投資の魔術」の P.166 図21 と同じものを作成してみました。
同じ期間と銘柄でチャートを出してみたものの、おそらく「つなぎ足と限月の違い」があり、まったく同じチャートにはなりませんでした。厳密には「同じ銘柄がなかった」と言うべきでしょうか。もしかしたら、「SMAとEMA」の違いもあるかもしれません。
ただ、近い形にはなっているため「大きな問題はないだろう」と判断しています。
パラメーターやコードは「Pineスクリプト」をご覧ください。
エントリーとイグジット
買いエントリー
- 「EMA350+ATR20の7倍」を終値で上抜けたら買いエントリー
- 買い持ちの状態で「EMA350」を終値で下抜けたら決済
売りエントリー
- 「EMA350-ATR20の3倍」を終値で下抜けたら売りエントリー
- 売り持ちの状態で「EMA350」を終値で上抜けたら決済
サンプルとして、きっちり獲れてる場面を掲載していますが、割と獲れない場面の方が多いです。ただ、獲れるときは、獲れないときの何倍も獲れる。「The・トレンドフォロー」ですね。
バックテスト
つくったストラテジーで実際にテストをしてみました。同じ条件で手法を比べるために、このシリーズでは以下の「基本条件」でテストしていくこととします。
バックテストの基本条件
期間
- 2005年1月1日 ~ 2017年12月31日(12年間)
資金管理
- 単利
銘柄
為替 | USDJPY、EURJPY、GBPJPY、CHFJPY、CADJPY |
株価指数 | NKY日経225、DJI NYダウ、DAXドイツ、UKXイギリスFTSE、HSI香港ハンセン |
日本株 | 6098リクルート、4452花王、5711三菱マテリアル、7201日産、9984ソフトバンクグループ |
米株 | AAPLアップル、AXPアメリカン・エクスプレス、BAボーイング、JNJジョンソン・エンド・ジョンソン、MCDマクドナルド |
海外商品 | 金、白金、原油、コーン、大豆 |
手法は、市場との相性があるケースも多いので、主要な各市場から5銘柄ずつピックアップしてテストしています。
テスト1
まずは、「本に掲載されていたものと同じ」テストを行います。あくまでも、本から読み取れる情報をもとに「こうだろうな」と設定したものです。
テスト1 設定
MA | ATR | Upper | Lower | Losscut | Pyramiding |
---|---|---|---|---|---|
350 | 20 | 7 | 3 | None | None |
テスト1 結果
損益 | 最大DD | 取引数 | 勝数 | 勝率 | RR比 | |
---|---|---|---|---|---|---|
Total | 383 | 113 | 29.50 | 4.090 | ||
USDJPY | 23.80 | 23.14 | 15 | 5 | 33.33 | 3.441 |
EURJPY | 38.13 | 17.03 | 16 | 8 | 50.00 | 1.978 |
GBPJPY | 162.40 | 8.92 | 9 | 5 | 55.56 | 7.959 |
CHFJPY | 8.12 | 22.97 | 15 | 3 | 20.00 | 4.954 |
CADJPY | -2.05 | 19.53 | 17 | 5 | 29.41 | 2.288 |
NKY | 8494.24 | 6386.40 | 19 | 5 | 26.32 | 5.364 |
DJI | 9202.67 | 1345.76 | 13 | 5 | 34.46 | 5.095 |
DAX | 1688.00 | 3206.79 | 16 | 6 | 37.50 | 1.946 |
UKX | -431.40 | 3254.80 | 17 | 4 | 23.53 | 3.108 |
HSI | 2072.38 | 16969.29 | 18 | 5 | 27.78 | 3.057 |
6098 | 1179.50 | 80.00 | 2 | 1 | 50.00 | 15.744 |
4452 | 794.00 | 1647.00 | 18 | 4 | 22.22 | 4.323 |
5711 | -1095.00 | 3425.00 | 20 | 6 | 25.00 | 2.377 |
7201 | -2113.20 | 2113.20 | 30 | 1 | 3.33 | 5.230 |
9984 | 2703.00 | 2070.00 | 16 | 6 | 27.50 | 3.018 |
AAPL | 76.65 | 30.10 | 12 | 5 | 41.67 | 3.747 |
BA | 186.10 | 39.07 | 12 | 5 | 41.67 | 5.994 |
AXP | 73.63 | 7.64 | 9 | 5 | 41.67 | 5.994 |
JNJ | 1.49 | 40.79 | 18 | 3 | 16.67 | 5.925 |
MCD | 57.96 | 22.34 | 12 | 4 | 33.33 | 5.720 |
GOLD | 88610 | 19470 | 8 | 4 | 50.00 | 3.056 |
PLATINUM | 36915 | 16315 | 15 | 5 | 33.33 | 5.796 |
WTI | 110 | 61140 | 19 | 5 | 26.32 | 3.265 |
CORN | 3287.5 | 9750 | 16 | 4 | 25.00 | 3.948 |
SOY BEANS | -6425 | 30412 | 21 | 4 | 19.05 | 4.026 |
25銘柄12年間のテスト。サンプル数は383トレードと少し心もとないですね。
テスト1 統計
破産の確率 | 期待値/リスク |
---|---|
0.00% | 0.5 |
破産の確率は、損失の許容=2%で算出しています。期待値と収益の見込みは以下の計算式で算出しています。
簡易的に計算した期待値ですが、プラスになりました。破産の確率もゼロ。おおむねほとんどの銘柄で損益プラスの成績。日本株が、他の市場と比べると不向きかもしれません。
TradingViewのバックテストには、残念ながら「年ごとの統計をとる機能」がないため詳細がわからないのですが、ものの数十分でイチからこれだけのテストができるのは非常に優秀だと思います。
テスト2
次に、ピラミッティングのギミックを追加して "すこし攻撃的な手法" にしてみます。
テスト2 設定
MA | ATR | Upper | Lower | Losscut | Pyramiding |
---|---|---|---|---|---|
350 | 20 | 7 | 3 | None | 10-ATR 上限4 |
今回加えたピラミッティングは、「エントリーした価格からプラス方向にATRの10倍動いたらポジションを追加する」というものです。それを3回行い、プラス方向に大きく値段が動いた場合、最初のエントリーを含めて「4になる」ということです。
テスト2 結果
損益 | 最大DD | 取引数 | 勝数 | 勝率 | RR比 | |
---|---|---|---|---|---|---|
Total | 591 | 235 | 39.76 | 3.100 | ||
USDJPY | 72.78 | 32.87 | 20 | 8 | 40.00 | 3.710 |
URJPY | -3.18 | 32.21 | 27 | 10 | 37.04 | 1.644 |
GBPJPY | 327.20 | 25.43 | 21 | 14 | 66.67 | 3.689 |
CHFJPY | 15.12 | 48.63 | 22 | 6 | 27.27 | 3.296 |
CADJPY | -56.29 | 64.30 | 28 | 9 | 32.14 | 0.983 |
NKY | 27987.02 | 10273.46 | 29 | 12 | 41.38 | 4.917 |
DJI | 16318.82 | 6114.77 | 26 | 13 | 50.00 | 2.823 |
DAX | 6360.26 | 3557.44 | 25 | 12 | 48.00 | 2.087 |
UKX | -368.50 | 5365.40 | 25 | 9 | 36 | 2.199 |
HSI | 5674.91 | 39666.1 | 18 | 5 | 27.78 | 3.057 |
6098 | 4095 | 80 | 5 | 4 | 80.00 | 13.047 |
4452 | 8769 | 1793 | 25 | 9 | 37.50 | 5.522 |
5711 | -3155 | 8070 | 29 | 10 | 34.48 | 1.338 |
7201 | -3057 | 3057 | 35 | 2 | 5.71 | 2.597 |
9984 | 2802 | 4654 | 27 | 10 | 37.04 | 2.264 |
AAPL | 252.59 | 59.72 | 26 | 15 | 57.69 | 3.034 |
AXP | 109.16 | 14,78 | 17 | 11 | 64.71 | 2.222 |
BA | 510.62 | 46.68 | 24 | 14 | 58.33 | 4.417 |
JNJ | 48.62 | 41.33 | 23 | 7 | 30.43 | 4.188 |
MCD | 135.3 | 32.16 | 21 | 11 | 52.38 | 3.568 |
GOLD | 259110 | 19470 | 15 | 11 | 73.33 | 2.666 |
PLATINUM | 36540 | 41000 | 26 | 11 | 42.31 | 2.078 |
WTI | -5630 | 91700 | 27 | 8 | 29.63 | 2.285 |
CORN | -22600 | 33025 | 23 | 6 | 26.09 | 1.449 |
SOY BEANS | 16112.5 | 54475 | 27 | 8 | 29.63 | 2.875 |
TOTALの勝率が「29.5% → 39.76%」と向上。一方でRR比は「4.09 → 3.1」と悪い数値に。これらは、2つの〝バランス〟が大切で、この〝バランス〟を「破産の確率」や「期待値」として表現することができます。
テスト2 統計
破産の確率 | 期待値/リスク |
---|---|
0.00% | 0.63 |
破産の確率は、損失の許容=2%で算出しています。期待値と収益の見込みは以下の計算式で算出しています。
テスト1と比較すると、期待値「0.5 → 0.63」と向上。破産の確率の悪化も見られないため、ピラミッティングを導入することによって「成績の向上が期待できそう」だということが言えそうです。
考察
ふたつのテストをそれぞれ〝25銘柄・12年〟で行いました。長期の手法であるためサンプル数(トレードの回数)は少し物足りないですが、サンプルの〝質〟は十分であると考えています。
今回テストを行った「ATRチャネル・ブレイクアウト」は10年以上も前に紹介されたものですが、「現在でも通用しそうだ」ということが言えそうですね。また、ピラミッティングの導入などで、まだまだ成績の向上も見込めそうです。
また、テスト結果から「どれくらいの利益を見込める手法なのか」を算出することもできます。(たぬきの皮算用)
テスト2の結果
- トレード回数:591回
- 期待値:リスクに対して0.63倍
これに対して、以下の条件でトレードする
- 元金100万円
- 単利
- 1トレードのリスクを2%(2万円)に
2万円 ✕ 0.63 ✕ 591トレード = 7,446,600円
単利で200万円を750万円弱にできた手法だということがわかります。もっとリアルな検証をする必要はありますが、〝期間ごとの複利〟を取り入れることでもっと大きな利益を狙うこともできそうです。
念のため補足しておくと、このバックテストは明確な損切りを設定しているものじゃないので、上記のようにリスクを固定することは少しむずかしい です。これも、 〝リアルなバックテスト〟をしてみると、もうちょっと見えてくるポイントですね。
Pineスクリプト
今回行ったバックテストに用いたストラテジーのコード(Pineスクリプト)を公開します。販売や二次配布以外は自由にご利用いただいて差し支えありません。ご自由にお使いください!
strategy("Strategy Turtle ATR Chanel Break Out"
,default_qty_type=strategy.fixed
,default_qty_value=1
,pyramiding=4
,overlay=true)
src = close
len = input(350 ,minval=1 ,title="ma length")
atr_len = input(20 ,minval=1 ,title="band atr length")
up_n = input(7 ,minval=1 ,title="atr upper length")
low_n = input(3 ,minval=1 ,title="atr lower length")
SO_bool = input(false,type=bool ,title="loss cut")
SO_len = input(20 ,type=integer ,minval=1 ,title="loss cut ATR length")
SO_N = input(2 ,type=float ,minval=0.5 ,title="loss cut ATR*N")
MAX_N = input(1 ,type=integer ,minval=1 ,maxval=4 ,title="maximun num of unit")
LO_len = input(20 ,type=integer ,minval=1 ,title="pyramiding ATR length")
LO_N = input(10 ,type=float ,minval=0.5 ,title="pyramiding ATR*N")
Tm_bool = input(false,type=bool ,title="timed exit")
Tm_len = input(80 ,type=integer ,minval=1 ,title="timed exit length")
fromYear = input(2005 ,type=integer ,minval=1900 ,title="test start")
endYear = input(2017 ,type=integer ,minval=1900 ,title="test end")
isWork = timestamp(fromYear ,1 ,1 ,00 ,00) <= time and time < timestamp(endYear+1 ,1 ,1 ,00 ,00)
EMA = ema(close ,len)
ATR = ema(tr ,atr_len)
UPPER = EMA + ATR * up_n
LOWER = EMA - ATR * low_n
atr_SO_ = ema(tr ,SO_len)
atr_LO_ = ema(tr ,LO_len)
atr_SO = atr_SO_*SO_N
atr_LO = atr_LO_*LO_N
countTradingDays = na
countNonTradingDays = na
countTradingDays := strategy.position_size==0 ? 0 : countTradingDays[1] + 1
countNonTradingDays := strategy.position_size!=0 ? 0 : countNonTradingDays[1] + 1
entry1 = close
entry2 = close
entry3 = close
entry4 = close
entry1 := strategy.position_size==0 ? na : entry1[1]
entry2 := strategy.position_size==0 ? na : entry2[1]
entry3 := strategy.position_size==0 ? na : entry3[1]
entry4 := strategy.position_size==0 ? na : entry4[1]
lo2 = close
lo3 = close
lo4 = close
lo2 := strategy.position_size==0 ? na : lo2[1]
lo3 := strategy.position_size==0 ? na : lo3[1]
lo4 := strategy.position_size==0 ? na : lo4[1]
losscut = close
losscut := strategy.position_size==0 or SO_bool==false ? na : losscut[1]
L_EntrySig = close >= UPPER and isWork
S_EntrySig = close <= LOWER and isWork
if(strategy.position_size != 0)
L_ExitSig = (close <= EMA or S_EntrySig) and strategy.position_size > 0
S_ExitSig = (close >= EMA or L_EntrySig) and strategy.position_size < 0
TimedSig = countTradingDays > Tm_len and Tm_bool
strategy.close_all(when = L_ExitSig or S_ExitSig or TimedSig or not isWork)
if(L_ExitSig or S_ExitSig)
entry1 := na
entry2 := na
entry3 := na
entry4 := na
lo2 := na
lo3 := na
lo4 := na
losscut := na
if(strategy.position_size > 0)
lo_sig2 = lo2 < high
lo_sig3 = lo3 < high
lo_sig4 = lo4 < high
if(lo_sig2 and MAX_N >= 2)
if(SO_bool)
strategy.entry("L-Entry2" ,strategy.long ,stop=close-atr_SO ,comment="L-Entry2")
strategy.exit("L-Entry1" ,stop=close-atr_SO)
else
strategy.entry("L-Entry2" ,strategy.long ,comment="L-Entry2")
lo2 := na
losscut := SO_bool ? close - atr_SO : na
if(lo_sig3 and MAX_N >= 3)
if(SO_bool)
strategy.entry("L-Entry3" ,strategy.long ,stop=close-atr_SO ,comment="L-Entry3")
strategy.exit("L-Entry2" ,stop=close-atr_SO)
strategy.exit("L-Entry1" ,stop=close-atr_SO)
else
strategy.entry("L-Entry3" ,strategy.long ,comment="L-Entry3")
lo3 := na
losscut := SO_bool ? close - atr_SO : na
if(lo_sig4 and MAX_N >= 4)
if(SO_bool)
strategy.entry("L-Entry4" ,strategy.long ,stop=close-atr_SO ,comment="L-Entry4")
strategy.exit("L-Entry3" ,stop=close-atr_SO)
strategy.exit("L-Entry2" ,stop=close-atr_SO)
strategy.exit("L-Entry1" ,stop=close-atr_SO)
else
strategy.entry("L-Entry4" ,strategy.long ,comment="L-Entry4")
lo4 := na
losscut := SO_bool ? close - atr_SO : na
if(strategy.position_size < 0)
lo_sig2 = lo2 > low
lo_sig3 = lo3 > low
lo_sig4 = lo4 > low
if(lo_sig2 and MAX_N >= 2)
if(SO_bool)
strategy.entry("S-Entry2" ,strategy.short ,stop=close+atr_SO ,comment="S-Entry2")
strategy.exit("S-Entry1" ,stop=close+atr_SO)
else
strategy.entry("S-Entry2" ,strategy.short ,comment="S-Entry2")
lo2 := na
losscut := SO_bool ? close + atr_SO : na
if(lo_sig3 and MAX_N >= 3)
if(SO_bool)
strategy.entry("S-Entry3" ,strategy.short ,stop=close+atr_SO ,comment="S-Entry3")
strategy.exit("S-Entry2" ,stop=close+atr_SO)
strategy.exit("S-Entry1" ,stop=close+atr_SO)
else
strategy.entry("S-Entry3" ,strategy.short ,comment="S-Entry3")
lo3 := na
losscut := SO_bool ? close + atr_SO : na
if(lo_sig4 and MAX_N >= 4)
if(SO_bool)
strategy.entry("S-Entry4" ,strategy.short ,stop=close+atr_SO ,comment="S-Entry4")
strategy.exit("S-Entry3" ,stop=close+atr_SO)
strategy.exit("S-Entry2" ,stop=close+atr_SO)
strategy.exit("S-Entry1" ,stop=close+atr_SO)
else
strategy.entry("S-Entry4" ,strategy.short ,comment="S-Entry4")
lo4 := na
losscut := SO_bool ? close + atr_SO : na
if((L_EntrySig or S_EntrySig) and strategy.position_size==0)
countTradingDays := 0
entry1 := close
if(L_EntrySig)
if(SO_bool)
strategy.entry("L-Entry1" ,strategy.long ,stop=close-atr_SO ,comment="L-Entry1")
else
strategy.entry("L-Entry1" ,strategy.long ,comment="L-Entry1")
lo2 := MAX_N >= 2 ? close + atr_LO : na
lo3 := MAX_N >= 3 ? close + atr_LO * 2 : na
lo4 := MAX_N >= 4 ? close + atr_LO * 3 : na
losscut := SO_bool ? close - atr_SO : na
if(S_EntrySig)
if(SO_bool)
strategy.entry("S-Entry1" ,strategy.short ,stop=close+atr_SO ,comment="S-Entry1")
else
strategy.entry("S-Entry1" ,strategy.short ,comment="S-Entry1")
lo2 := MAX_N >= 2 ? close - atr_LO : na
lo3 := MAX_N >= 3 ? close - atr_LO * 2 : na
lo4 := MAX_N >= 4 ? close - atr_LO * 3 : na
losscut := SO_bool ? close + atr_SO : na
plot(strategy.position_size ,transp=0 ,title="保有ポジションの数")
plot(strategy.openprofit ,transp=0 ,title="未決済の損益")
plot(strategy.netprofit ,transp=0 ,title="決済済みの損益")
plot(strategy.closedtrades ,transp=0 ,title="決済済み取引数")
plot(countTradingDays ,transp=0 ,title="取引日数")
plot(countNonTradingDays ,transp=0 ,title="ノンポジ日数")
plot(entry1 ,title="entry1" ,color=blue ,transp=0 ,style=linebr)
plot(lo2 ,title="lo2" ,color=red ,transp=0 ,style=linebr)
plot(lo3 ,title="lo3" ,color=red ,transp=0 ,style=linebr)
plot(lo4 ,title="lo4" ,color=red ,transp=0 ,style=linebr)
plot(losscut ,title="losscut" ,color=red ,transp=0 ,style=linebr)
plot(atr_SO ,transp=0 ,title="ATR_SO")
plot(atr_LO ,transp=0 ,title="ATR_LO")
p1 = plot(UPPER ,color=#303F9F ,title="UPPER" ,style=line ,linewidth=2, transp=0)
p2 = plot(LOWER ,color=#4CAF50 ,title="LOWER" ,style=line ,linewidth=2, transp=0)
plot(EMA ,color=red ,title="EMA" ,style=line ,linewidth=2 ,transp=0)
fill(p1 ,p2 ,color=#2196F3 ,title="fill" ,transp=60)
TradingViewでも公開しています!
TradingView のインジケーターの検索で「ATR chanel」と検索するとでてきます。上記のコードと同じストラテジーを使用することができます。
バックテストならTradingView
TradingViewのテストは大変便利で、かなり自由の効くバックテストを短時間で簡単に、価格データを用意することなく豊富な銘柄と足種を対象に行うことができます。
一方で、その簡便さと引き換えに、TradingViewではできないバックテストも多くあります。
たとえば「分散投資」や「資金管理」があげられますが、これらは投資において非常に重要な要素でもあり、これらを含めたバックテストをするならリアルさが不可欠です。こういったバックテストをするためには、や はりPythonなどのプログラミング言語で自作していくしかないと考えています。
とはいえ、TradingViewが便利であることは間違いなく、最近では、手法の検証はTradingViewで行い、良い手法が見つかったら更に詳細なテストをPythonで行うようにしています。できるものはTradingViewでサクサクやってしまいます。
ちなみに、機能的には無料アカウントで十分ですが、個別株の分析を行う際には有料アカウントが必要になります。
- 記事をシェア