TradingViewで検証:ATRチャネル・ブレイクアウト

Posted on December 2nd, 2018Updated on May 4th, 2019
TradingViewで検証:ATRチャネル・ブレイクアウト

※ この記事は最終更新日から5年以上が経過しています。

どんな記事

この記事を読むと次のことができる(わかる)ようになります。

  • 「投資の魔術」掲載の手法を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 )フォーラムのトレーダー、マーク・ジョンソンが普及させた。これは次に述べるボリンジャー・ブレイクアウト・システムの変形でもある。

引用元: 伝説のトレーダー集団 タートルズ流投資の魔術(P.165)

この記事は、上記の手法をTradingViewで再現することを目的としたものです。

ストラテジーを作成

TradingView ATRチャネル・ブレイクアウト・ストラテジー

チャートが再現できているか確認するために、「伝説のトレーダー集団 タートルズ流投資の魔術」の P.166 図21 と同じものを作成してみました。

同じ期間と銘柄でチャートを出してみたものの、おそらく「つなぎ足と限月の違い」があり、まったく同じチャートにはなりませんでした。厳密には「同じ銘柄がなかった」と言うべきでしょうか。もしかしたら、「SMAとEMA」の違いもあるかもしれません。

ただ、近い形にはなっているため「大きな問題はないだろう」と判断しています。

パラメーターやコードは「Pineスクリプト」をご覧ください。

エントリーとイグジット

買いエントリー

  • 「EMA350+ATR20の7倍」を終値で上抜けたら買いエントリー
  • 買い持ちの状態で「EMA350」を終値で下抜けたら決済
※ EMA●●:●●日指数平滑移動平均
TradingView ATRチャネル・ブレイクアウト・ストラテジー 買いシグナルとイグジット

売りエントリー

  • 「EMA350-ATR20の3倍」を終値で下抜けたら売りエントリー
  • 売り持ちの状態で「EMA350」を終値で上抜けたら決済
TradingView ATRチャネル・ブレイクアウト・ストラテジー 売りシグナルとイグジット

サンプルとして、きっちり獲れてる場面を掲載していますが、割と獲れない場面の方が多いです。ただ、獲れるときは、獲れないときの何倍も獲れる。「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 設定

MAATRUpperLowerLosscutPyramiding
3502073NoneNone

テスト1 結果

損益最大DD取引数勝数勝率RR比
Total38311329.504.090
USDJPY23.8023.1415533.333.441
EURJPY38.1317.0316850.001.978
GBPJPY162.408.929555.567.959
CHFJPY8.1222.9715320.004.954
CADJPY-2.0519.5317529.412.288
NKY8494.246386.4019526.325.364
DJI9202.671345.7613534.465.095
DAX1688.003206.7916637.501.946
UKX-431.403254.8017423.533.108
HSI2072.3816969.2918527.783.057
60981179.5080.002150.0015.744
4452794.001647.0018422.224.323
5711-1095.003425.0020625.002.377
7201-2113.202113.203013.335.230
99842703.002070.0016627.503.018
AAPL76.6530.1012541.673.747
BA186.1039.0712541.675.994
AXP73.637.649541.675.994
JNJ1.4940.7918316.675.925
MCD57.9622.3412433.335.720
GOLD88610194708450.003.056
PLATINUM369151631515533.335.796
WTI1106114019526.323.265
CORN3287.5975016425.003.948
SOY BEANS-64253041221419.054.026

25銘柄12年間のテスト。サンプル数は383トレードと少し心もとないですね。

テスト1 統計

破産の確率期待値/リスク
0.00%0.5

破産の確率は、損失の許容=2%で算出しています。期待値と収益の見込みは以下の計算式で算出しています。

期待値=勝率×RR比1勝率\text{期待値} = \text{勝率} \times \text{RR比} - | 1 - \text{勝率}|

簡易的に計算した期待値ですが、プラスになりました。破産の確率もゼロ。おおむねほとんどの銘柄で損益プラスの成績。日本株が、他の市場と比べると不向きかもしれません。

TradingViewのバックテストには、残念ながら「年ごとの統計をとる機能」がないため詳細がわからないのですが、ものの数十分でイチからこれだけのテストができるのは非常に優秀だと思います。

テスト2

次に、ピラミッティングのギミックを追加して "すこし攻撃的な手法" にしてみます。

テスト2 設定

MAATRUpperLowerLosscutPyramiding
3502073None10-ATR 上限4

今回加えたピラミッティングは、「エントリーした価格からプラス方向にATRの10倍動いたらポジションを追加する」というものです。それを3回行い、プラス方向に大きく値段が動いた場合、最初のエントリーを含めて「4になる」ということです。

テスト2 結果

損益最大DD取引数勝数勝率RR比
Total59123539.763.100
USDJPY72.7832.8720840.003.710
URJPY-3.1832.21271037.041.644
GBPJPY327.2025.43211466.673.689
CHFJPY15.1248.6322627.273.296
CADJPY-56.2964.3028932.140.983
NKY27987.0210273.46291241.384.917
DJI16318.826114.77261350.002.823
DAX6360.263557.44251248.002.087
UKX-368.505365.40259362.199
HSI5674.9139666.118527.783.057
60984095805480.0013.047
44528769179325937.505.522
5711-31558070291034.481.338
7201-305730573525.712.597
998428024654271037.042.264
AAPL252.5959.72261557.693.034
AXP109.1614,78171164.712.222
BA510.6246.68241458.334.417
JNJ48.6241.3323730.434.188
MCD135.332.16211152.383.568
GOLD25911019470151173.332.666
PLATINUM3654041000261142.312.078
WTI-56309170027829.632.285
CORN-226003302523626.091.449
SOY BEANS16112.55447527829.632.875

TOTALの勝率が「29.5% → 39.76%」と向上。一方でRR比は「4.09 → 3.1」と悪い数値に。これらは、2つの〝バランス〟が大切で、この〝バランス〟を「破産の確率」や「期待値」として表現することができます。

テスト2 統計

破産の確率期待値/リスク
0.00%0.63

破産の確率は、損失の許容=2%で算出しています。期待値と収益の見込みは以下の計算式で算出しています。

期待値=勝率×RR比1勝率\text{期待値} = \text{勝率} \times \text{RR比} - | 1 - \text{勝率}|

テスト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スクリプト)を公開します。販売や二次配布以外は自由にご利用いただいて差し支えありません。ご自由にお使いください!

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 breakout」を見つける

TradingView のインジケーターの検索で「ATR chanel」と検索するとでてきます。上記のコードと同じストラテジーを使用することができます。

バックテストならTradingView

TradingViewのテストは大変便利で、かなり自由の効くバックテストを短時間で簡単に、価格データを用意することなく豊富な銘柄と足種を対象に行うことができます。

一方で、その簡便さと引き換えに、TradingViewではできないバックテストも多くあります。

たとえば「分散投資」や「資金管理」があげられますが、これらは投資において非常に重要な要素でもあり、これらを含めたバックテストをするならリアルさが不可欠です。こういったバックテストをするためには、やはりPythonなどのプログラミング言語で自作していくしかないと考えています。

とはいえ、TradingViewが便利であることは間違いなく、最近では、手法の検証はTradingViewで行い、良い手法が見つかったら更に詳細なテストをPythonで行うようにしています。できるものはTradingViewでサクサクやってしまいます。

ちなみに、機能的には無料アカウントで十分ですが、個別株の分析を行う際には有料アカウントが必要になります。

yuya takahashi

タカハシ / 11年目の兼業投資家

投資やプログラミング、動画コンテンツの撮影・制作・編集などが得意。更新のお知らせは、LINE、メールで行っています。

このブログと筆者についてご質問はこちら

  • 記事をシェア
© Investment Tech Hack 2023.