Created
November 21, 2020 05:53
-
-
Save sin32775/071d2962873b2aa0695c91900773bc11 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"<h2>ドンチャンブレイクアウトとは</h2>\n", | |
"<p>ブレイクアウト手法とは、狭い値幅での揉み合い(レンジ相場)をブレイクアウトした瞬間を捕まえてエントリーし、その後のトレンドに乗って利益を出す順張り(トレンドフォロー型)の手法のことをいいます。</p>\n", | |
"<p>ブレイクアウトを捉えるための手法はさまざまなものが開発されていますが、シストレなどの自動売買BOTの世界では、以下の2つが最も定番です。</p>\n", | |
"<h3>1.ドンチャン・チャネル・ブレイクアウト</h3>\n", | |
"<p>過去n期間の最高値・最安値を更新したときに、その方向にエントリーする手法です。</p>\n", | |
"<p>例えば、過去20期間の最高値を更新したときに「買い」でエントリーします。次に過去20日の最安値を更新したときに決済して手仕舞いますが、このときさらに反対方向にそのままエントリー(ドテン)する場合も多いです。</p>\n", | |
"<p><img src=\"https://ryota-trade.com/wp-content/uploads/2018/04/backtest_donchian_break.jpg\" alt=\"\" width=\"640\" height=\"445\" class=\"alignnone size-full wp-image-2312\" srcset=\"https://ryota-trade.com/wp-content/uploads/2018/04/backtest_donchian_break.jpg 640w, https://ryota-trade.com/wp-content/uploads/2018/04/backtest_donchian_break-300x209.jpg 300w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n", | |
"<p>このシンプルな戦略の優位性は、トレーダーの必読本である「<a href=\"https://www.amazon.co.jp/%E4%BC%9D%E8%AA%AC%E3%81%AE%E3%83%88%E3%83%AC%E3%83%BC%E3%83%80%E3%83%BC%E9%9B%86%E5%9B%A3-%E3%82%BF%E3%83%BC%E3%83%88%E3%83%AB%E6%B5%81%E6%8A%95%E8%B3%87%E3%81%AE%E9%AD%94%E8%A1%93-%E3%82%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%82%B9%E3%83%BB%E3%83%95%E3%82%A7%E3%82%A4%E3%82%B9/dp/4198624267\" rel=\"noopener\" target=\"_blank\">タートル流投資の魔術</a>」や、ジョン・ヒルの「<a href=\"https://www.amazon.co.jp/%E7%A9%B6%E6%A5%B5%E3%81%AE%E3%83%88%E3%83%AC%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%AC%E3%82%A4%E3%83%89-%E5%85%A8%E7%B1%B3%E4%B8%80%E3%81%AE%E6%8A%95%E8%B3%87%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E5%88%86%E6%9E%90%E5%AE%B6%E3%81%8C%E6%98%8E%E3%81%8B%E3%81%99%E3%80%8C%E5%84%B2%E3%81%8B%E3%82%8B%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%80%8D-%E3%82%A6%E3%82%A3%E3%82%B6%E3%83%BC%E3%83%89%E3%83%96%E3%83%83%E3%82%AF%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E3%82%B8%E3%83%A7%E3%83%B3%E3%83%BBR%E3%83%BB%E3%83%92%E3%83%AB/dp/4775970151\" rel=\"noopener\" target=\"_blank\">究極のトレーディングガイド</a>」などの本に詳しい解説があります。</p>\n", | |
"<p>非常に単純な売買ロジックなので、プログラミングしやすく、シストレや自動売買BOTの勉強にも最適です。</p>\n", | |
"<h3>2.オープニングレンジ・ブレイクアウト</h3>\n", | |
"<p>オープニングレンジ・ブレイクアウトも同じく「どうやったらブレイクアウトの開始点を、シンプルな数字とロジックで発見できるか?」という視点で開発された手法です。</p>\n", | |
"<p>こちらは、過去n期間の値幅の平均値(ATR)を基準値として利用し、ある足が始値から(基準値の)X%以上動いたらその方向にエントリーします。例えば、過去3期間の安値–高値の平均値が10万円でXが70%とすると、次の足で始値から7万円上に動いた時点で買いでエントリーします。</p>\n", | |
"<p><img src=\"https://ryota-trade.com/wp-content/uploads/2018/04/backtest_openingbreak.jpg\" alt=\"\" width=\"640\" height=\"496\" class=\"alignnone size-full wp-image-2314\" srcset=\"https://ryota-trade.com/wp-content/uploads/2018/04/backtest_openingbreak.jpg 640w, https://ryota-trade.com/wp-content/uploads/2018/04/backtest_openingbreak-300x233.jpg 300w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n", | |
"<p>前の足が陰線か陽線かで場合分けをして、買いと売りとで別々のX%のパラメーターを使用することが多いです。</p>\n", | |
"<p>この戦略については、ラリーウィリアムズの「<a href=\"https://www.amazon.co.jp/%E3%83%A9%E3%83%AA%E3%83%BC%E3%83%BB%E3%82%A6%E3%82%A3%E3%83%AA%E3%82%A2%E3%83%A0%E3%82%BA%E3%81%AE%E7%9F%AD%E6%9C%9F%E5%A3%B2%E8%B2%B7%E6%B3%95%E3%80%90%E6%94%B9%E5%AE%9A%E7%AC%AC2%E7%89%88%E3%80%91-%E3%82%A6%E3%82%A3%E3%82%B6%E3%83%BC%E3%83%89%E3%83%96%E3%83%83%E3%82%AF-%E3%83%A9%E3%83%AA%E3%83%BC%E3%83%BB%E3%82%A6%E3%82%A3%E3%83%AA%E3%82%A2%E3%83%A0%E3%82%BA/dp/4775971603\" rel=\"noopener\" target=\"_blank\">短期売買法</a>」や、先ほどのジョンヒルの「<a href=\"https://www.amazon.co.jp/%E7%A9%B6%E6%A5%B5%E3%81%AE%E3%83%88%E3%83%AC%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%AC%E3%82%A4%E3%83%89-%E5%85%A8%E7%B1%B3%E4%B8%80%E3%81%AE%E6%8A%95%E8%B3%87%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E5%88%86%E6%9E%90%E5%AE%B6%E3%81%8C%E6%98%8E%E3%81%8B%E3%81%99%E3%80%8C%E5%84%B2%E3%81%8B%E3%82%8B%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%80%8D-%E3%82%A6%E3%82%A3%E3%82%B6%E3%83%BC%E3%83%89%E3%83%96%E3%83%83%E3%82%AF%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E3%82%B8%E3%83%A7%E3%83%B3%E3%83%BBR%E3%83%BB%E3%83%92%E3%83%AB/dp/4775970151\" rel=\"noopener\" target=\"_blank\">究極のトレーディングガイド</a>」などの本に詳しい解説があります。</p>\n", | |
"<h3>3.戦略の使い方</h3>\n", | |
"<p>どちらも多くのトレーダーが知っている手法ですが、実際には、どの時間軸でどういうパラメーターを使うか、どういうトレンド判定の条件やフィルターと組み合わて「騙し」を除去するか、どういう手仕舞いの方法を選択するか、などによって全く違う成績になります。</p>\n", | |
"<p>これらの手法は、あくまで売買ロジックを考えるときにベースのアイデアとして使いやすいというだけで、それ自体が必勝法というわけではありません。詳しい戦略のカスタマイズ方法などは、今後、解説していく予定です。</p>\n", | |
"<p>今回はバックテストの方法の解説記事なので、まずは一番シンプルな「ドンチャン・ブレイクアウト」のロジックをpythonでコーディングするところから始めます。</p>\n", | |
"<p>ドンチャンブレイクアウトは非常に単純なロジックなので、今まで勉強した知識だけでも、pythonのプログラムコードを書くことができます。早速、やっていきましょう!</p>\n", | |
"<h2>ドンチャン・ブレイクアウトのpythonコード</h2>\n", | |
"<p>プログラミング初心者の方は、いきなり複雑なロジックを書こうとしてはいけません。まずは必要最小限のシンプルな骨組みの部分を作り、そこから徐々に条件などを足していくのがお勧めです。</p>\n", | |
"<p>例えば、買いと売りの両方を考えて混乱するならまずは買いエントリーだけ実装しましょう。エントリーと手仕舞いの両方を考えて混乱するなら、まずは「n足後に無条件で手仕舞う」といったシンプルなロジックで実装しましょう。</p>\n", | |
"<p>ではドンチャン・ブレイクアウトの最もシンプルなロジックとは何でしょうか? 今回は以下のように定義してみます。</p>\n", | |
"<h3>1.今回実装するロジック</h3>\n", | |
"<p>1)過去n期間の最高値を直近の足の高値が上回ったら(その足の終値の指値で)買いエントリーする。過去n期間の最安値を直近の足の安値が下回ったら売りエントリーする。<br />\n", | |
"2)買いエントリーをした場合、過去n期間の最安値を更新したら手仕舞い、さらに売りエントリーする<br />\n", | |
"3)売りエントリーをした場合、過去n期間の最高値を更新したら手仕舞い、さらに買いエントリーする</p>\n", | |
"<p>これだけです!</p>\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"<h2>パラメーター最適化とは</h2>\n", | |
"<p>パラメーターとは、ある自動売買BOTのロジックの中で自由に動かして変更できる値のことをいいます。n期間ドンチャン・ブレイクアウトBOTには、以下の2つのパラメーターがありました。</p>\n", | |
"<p>1)X分足の時間軸を使う<br />\n", | |
"2)n期間の最高値(最安値)ブレイクアウトで買う</p>\n", | |
"<p>同じドンチャンブレイクアウトでも、15分足で10期間のドンチャン・ブレイクアウト戦略を使うのと、1時間足で30期間のドンチャン・ブレイクアウト戦略を使うのとでは、全く成績が異なります。\n", | |
"<p>基本的には、数千通りくらいのパターンであれば、ただの「総当たり」で全パターンを計算すれば十分です。全パターンを試しても数十秒~1分もあれば終わりますので、複雑な機械学習や最適化アルゴリズムは必要ありません。</p>\n", | |
"<p>例えば、以下のようなパターンの組み合わせをすべて試したいとしましょう。</p>\n", | |
"<pre class=\"prettyprint\">\n", | |
"\n", | |
"# 4種類のパラメーター\n", | |
"paramA = [ 10,20,30,40,50 ]\n", | |
"paramB = [ 10,20,30,40,50 ]\n", | |
"paramC = [ \"A\" , \"B\" ]\n", | |
"paramD = [ 1,2,3,4,5,6,7,8,9,10 ]\n", | |
"\n", | |
"</pre>\n", | |
"<p>この場合、以下のようにfor文を書くことで全パターンの組み合わせを試すことができます。</p>\n", | |
"<pre class=\"prettyprint\">\n", | |
"\n", | |
"# for文で総当たりのパターンを記述\n", | |
"for a in paramA:\n", | |
"\tfor b in paramB:\n", | |
"\t\tfor c in paramC:\n", | |
"\t\t\tfor d in paramD:\n", | |
"\t\t\t\t\n", | |
"\t\t\t\t# 各組合せで実行したい処理\n", | |
"\n", | |
"\n", | |
"</pre>\n", | |
"<p>パラメーターAのfor文処理をするなかで、1つの要素についてさらにパラメーターBのfor文処理をし、パラメーターBのfor文処理のなかの1つの要素に対してさらにパラメーターCのfor文処理をし….という入れ子構造にしていくわけですね。</p>\n", | |
"<p><img src=\"https://ryota-trade.com/wp-content/uploads/2018/04/param_seek_ffor.jpg\" alt=\"\" width=\"640\" height=\"841\" class=\"alignnone size-full wp-image-2516\" srcset=\"https://ryota-trade.com/wp-content/uploads/2018/04/param_seek_ffor.jpg 640w, https://ryota-trade.com/wp-content/uploads/2018/04/param_seek_ffor-228x300.jpg 228w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n", | |
"<p>これを実行することで、5 × 5 × 2 × 10 = 500通りの組み合わせについて、全てのパターンでバックテストを実行することができます。</p>\n", | |
"<h3>シンプルな記述方法</h3>\n", | |
"<p>なお、pythonでは上記のコードをもっとシンプルにして以下のように書くことができます。</p>\n", | |
"<pre class=\"prettyprint\">\n", | |
"\n", | |
"combinations = [(a, b, c, d)\n", | |
"\tfor a in paramA\n", | |
"\tfor b in paramB\n", | |
"\tfor c in paramC\n", | |
"\tfor d in paramD]\n", | |
"\n", | |
"for a,b,c,d in combinations:\n", | |
"\t# 各組合せで実行したい処理\n", | |
"</pre>\n", | |
"<h3>試すパラメーターの組み合わせ</h3>\n", | |
"<p>なお、今回のドンチャンブレイクアウトのパラメーター最適化では、以下の組み合わせを総当たりで全て試してみることにします。</p>\n", | |
"<p>1)使用する時間軸<br />\n", | |
"⇒ 30分足/1時間足/2時間足/6時間足/12時間足/1日足</p>\n", | |
"<p>2)上値ブレイクアウトの判定期間<br />\n", | |
"⇒ 10/15/20/25/30/35/40/45</p>\n", | |
"<p>3)下値ブレイクアウトの判定期間<br />\n", | |
"⇒ 10/15/20/25/30/35/40/45</p>\n", | |
"<p>4)判定に使用する価格<br />\n", | |
"⇒ 高値・安値/終値・終値</p>\n", | |
"<p>時間足が6通り、判定期間が上値ブレイクアウト・下値ブレイクアウトでそれぞれ8通りずつ、判定に使用する価格が2通りで、合計 768通りのパターンをテストしてみましょう。</p>\n", | |
"<p>これをfor文の総当たりでテストするためには、以下のように書けばOKです。</p>\n", | |
"<pre class=\"prettyprint\">\n", | |
"\n", | |
"# バックテストのパラメーター設定\n", | |
"\n", | |
"chart_sec_list = [ 1800, 3600, 7200, 21600, 43200, 86400 ] # 時間足\n", | |
"buy_term_list = [ 10,15,20,25,30,35,40,45 ] # 上値ブレイクの判断期間\n", | |
"sell_term_list = [ 10,15,20,25,30,35,40,45 ] # 下値ブレイクの判断期間\n", | |
"judge_price_list = [\n", | |
"\t{\"BUY\":\"close_price\",\"SELL\":\"close_price\"}, # 終値/終値 と 高値/安値\n", | |
"\t{\"BUY\":\"high_price\",\"SELL\":\"low_price\"}\n", | |
"]\n", | |
"\n", | |
"# for文の記述方法\n", | |
"\n", | |
"combinations = [(chart_sec, buy_term, sell_term, judge_price)\n", | |
"\tfor chart_sec in chart_sec_list\n", | |
"\tfor buy_term in buy_term_list\n", | |
"\tfor sell_term in sell_term_list\n", | |
"\tfor judge_price in judge_price_list]\n", | |
"\n", | |
"for chart_sec, buy_term, sell_term,judge_price in combinations:\n", | |
"\t# 各回のバックテスト処理を記述\n", | |
"\t# (今までの記事で作成したもの)\n", | |
"\n", | |
"</pre>\n", | |
"<p>これで総当たりのfor文の準備は完了です!</p>\n", | |
"<h3>最大化したい指標を決める</h3>\n", | |
"<p>何のためにパラメーター最適化を実施するのかといえば、リターンを極限まで高くしたいからですよね。なので「総利益が最大になるようなパラメーターを探す」というのも1つの方法です。</p>\n", | |
"<p>しかし自動売買BOTの評価を最終損益だけで判断するのは不十分です。より厳密にいえば、私たちが探している理想の売買ロジックは、「できるだけリスクを抑えながら、できるだけ大きなリターンを得ること」にあるはずです。そのため、リスクとリターンの比率を1つの数字で表せるような指標を「最大化したい数字」に設定すべきです。</p>\n", | |
"<p>このような指標には、シャープレシオ、MARレシオなど、いくつかの指標がありますが、ここでは一番オーソドックスな「プロフィットファクター」を使うことにします。</p>\n", | |
"<h3>プロフィットファクター(PF)とは</h3>\n", | |
"<p>同じ最終利益100万円でも、「利益110万円 / 損失10万円」の売買システムと、「利益200万円 / 損失100万円」の売買システムとでは、かなり意味が違ってきます。</p>\n", | |
"<p>前者は、損失が利益のおよそ1/10で済んでいるのに対し、後者は、なんと利益の半分もの損失を出してしまっています。当然、前者の方が安定した売買システムで、後者のほうがより不安定な(リスクの高い)売買システムということになります。</p>\n", | |
"<p>このような、「 総利益 / 総損失 」で表される指標のことをプロフィットファクター(PF)といいます。PFが大きければ大きいほど、そのシステムは安定していてリスクが小さいと評価できます。</p>\n", | |
"\n", | |
"\n", | |
"# バックテストに必要な時間軸のチャートをすべて取得\n", | |
"price_list = {}\n", | |
"for chart_sec in chart_sec_list:\n", | |
"\tprice_list[ chart_sec ] = get_price(chart_sec,after=1451606400)\n", | |
"\tprint(\"-----{}分軸の価格データをCryptowatchから取得中-----\".format( int(chart_sec/60) ))\n", | |
"\ttime.sleep(10)\n", | |
"\n", | |
"# テストごとの各パラメーターの組み合わせと結果を記録する配列を準備\n", | |
"param_buy_term = []\n", | |
"param_sell_term = []\n", | |
"param_chart_sec = []\n", | |
"param_judge_price = []\n", | |
"\n", | |
"result_count = []\n", | |
"result_winRate = []\n", | |
"result_returnRate = []\n", | |
"result_drawdown = []\n", | |
"result_profitFactor = []\n", | |
"result_gross = []\n", | |
"\n", | |
"# 総当たりのためのfor文の準備\n", | |
"combinations = [(chart_sec, buy_term, sell_term, judge_price)\n", | |
"\tfor chart_sec in chart_sec_list\n", | |
"\tfor buy_term in buy_term_list\n", | |
"\tfor sell_term in sell_term_list\n", | |
"\tfor judge_price in judge_price_list]\n", | |
"\n", | |
"for chart_sec, buy_term, sell_term,judge_price in combinations:\n", | |
"\t\n", | |
"\tprice = price_list[ chart_sec ]\n", | |
"\tlast_data = []\n", | |
"\ti = 0\n", | |
"\t\n", | |
"\t# フラッグ変数の初期化\n", | |
"\tflag = {\n", | |
"\t\t\"order\":{\n", | |
"\t\t\t\"exist\" : False,\n", | |
"\t\t\t\"side\" : \"\",\n", | |
"\t\t\t\"price\" : 0,\n", | |
"\t\t\t\"count\" : 0\n", | |
"\t\t},\n", | |
"\t\t\"position\":{\n", | |
"\t\t\t\"exist\" : False,\n", | |
"\t\t\t\"side\" : \"\",\n", | |
"\t\t\t\"price\": 0,\n", | |
"\t\t\t\"count\":0\n", | |
"\t\t},\n", | |
"\t\t\"records\":{\n", | |
"\t\t\t\"date\":[],\n", | |
"\t\t\t\"profit\":[],\n", | |
"\t\t\t\"return\":[],\n", | |
"\t\t\t\"side\":[],\n", | |
"\t\t\t\"holding-periods\":[],\n", | |
"\t\t\t\"slippage\":[]\n", | |
"\t\t}\n", | |
"\t}\n", | |
"\t\n", | |
"\twhile i < len(price):\n", | |
"\t\t\n", | |
"\t\t# ドンチャンの判定に使う期間分の安値・高値データを準備する\n", | |
"\t\tif len(last_data) < buy_term or len(last_data) < sell_term:\n", | |
"\t\t\tlast_data.append(price[i])\n", | |
"\t\t\ttime.sleep(wait)\n", | |
"\t\t\ti += 1\n", | |
"\t\t\tcontinue\n", | |
"\t\t\n", | |
"\t\tdata = price[i]\n", | |
"\t\t\n", | |
"\t\tif flag[\"order\"][\"exist\"]:\n", | |
"\t\t\tflag = check_order( flag )\n", | |
"\t\telif flag[\"position\"][\"exist\"]:\n", | |
"\t\t\tflag = close_position( data,last_data,flag )\n", | |
"\t\telse:\n", | |
"\t\t\tflag = entry_signal( data,last_data,flag )\n", | |
"\t\t\n", | |
"\t\tlast_data.append( data )\n", | |
"\t\ti += 1\n", | |
"\t\ttime.sleep(wait)\n", | |
"\n", | |
"\n", | |
"\tprint(\"--------------------------\")\n", | |
"\tprint(\"テスト期間 :\")\n", | |
"\tprint(\"開始時点 : \" + str(price[0][\"close_time_dt\"]))\n", | |
"\tprint(\"終了時点 : \" + str(price[-1][\"close_time_dt\"]))\n", | |
"\tprint(\"時間軸 : \" + str(int(chart_sec/60)) + \"分足で検証\")\n", | |
"\tprint(\"パラメータ1 : \" + str(buy_term) + \"期間 / 買い\" )\n", | |
"\tprint(\"パラメータ2 : \" + str(sell_term) + \"期間 / 売り\" )\n", | |
"\tprint(str(len(price)) + \"件のローソク足データで検証\")\n", | |
"\tprint(\"--------------------------\")\n", | |
"\n", | |
"\t\n", | |
"\tresult = backtest( flag )\n", | |
"\t\n", | |
"\t\n", | |
"\t# 今回のループで使ったパラメータの組み合わせを配列に記録する\n", | |
"\tparam_buy_term.append( buy_term )\n", | |
"\tparam_sell_term.append( sell_term )\n", | |
"\tparam_chart_sec.append( chart_sec )\n", | |
"\tif judge_price[\"BUY\"] == \"high_price\":\n", | |
"\t\tparam_judge_price.append( \"高値/安値\" )\n", | |
"\telse:\n", | |
"\t\tparam_judge_price.append( \"終値/終値\" )\n", | |
"\t\n", | |
"\t\n", | |
"\t# 今回のループのバックテスト結果を配列に記録する\n", | |
"\tresult_count.append( result[\"トレード回数\"] )\n", | |
"\tresult_winRate.append( result[\"勝率\"] )\n", | |
"\tresult_returnRate.append( result[\"平均リターン\"] )\n", | |
"\tresult_drawdown.append( result[\"最大ドローダウン\"] )\n", | |
"\tresult_profitFactor.append( result[\"プロフィットファクタ―\"] )\n", | |
"\tresult_gross.append( result[\"最終損益\"] )\n", | |
"\t\n", | |
"\t\n", | |
"\n", | |
"# 全てのパラメータによるバックテスト結果をPandasで1つの表にする\n", | |
"df = pd.DataFrame({\n", | |
"\t\"時間軸\" : param_chart_sec,\n", | |
"\t\"買い期間\" : param_buy_term,\n", | |
"\t\"売り期間\" : param_sell_term,\n", | |
"\t\"判定基準\" : param_judge_price,\n", | |
"\t\"トレード回数\" : result_count,\n", | |
"\t\"勝率\" : result_winRate,\n", | |
"\t\"平均リターン\" : result_returnRate,\n", | |
"\t\"ドローダウン\" : result_drawdown,\n", | |
"\t\"PF\" : result_profitFactor,\n", | |
"\t\"最終損益\" : result_gross\n", | |
"})\n", | |
"\n", | |
"# 列の順番を固定する\n", | |
"df = df[[ \"時間軸\",\"買い期間\",\"売り期間\",\"判定基準\",\"トレード回数\",\"勝率\",\"平均リターン\",\"ドローダウン\",\"PF\",\"最終損益\" ]]\n", | |
"\n", | |
"# トレード回数が100に満たない記録は消す\n", | |
"df.drop( df[ df[\"トレード回数\"] < 100].index, inplace=True )\n", | |
"\n", | |
"# 最終結果をcsvファイルに出力\n", | |
"df.to_csv(\"result-{}.csv\".format(datetime.now().strftime(\"%Y-%m-%d-%H-%M\")) )\n", | |
"\n", | |
"</pre>\n", | |
"<h3>実行結果</h3>\n", | |
"<p>これを実行すると以下のようなCSVファイルが出力されます。</p>\n", | |
"<p><img src=\"https://ryota-trade.com/wp-content/uploads/2018/04/WS000457.jpg\" alt=\"\" width=\"874\" height=\"1019\" class=\"alignnone size-full wp-image-2479\" srcset=\"https://ryota-trade.com/wp-content/uploads/2018/04/WS000457.jpg 874w, https://ryota-trade.com/wp-content/uploads/2018/04/WS000457-257x300.jpg 257w, https://ryota-trade.com/wp-content/uploads/2018/04/WS000457-768x895.jpg 768w\" sizes=\"(max-width: 874px) 100vw, 874px\" /></p>\n", | |
"<p>プロフィットファクター(PF)のJ列を「降順」で並び変えてみると、ドンチャン・ブレイクアウトの有望なパラメーターの組み合わせがわかります。</p>\n", | |
"<h4>▽ PFで並び替え後の成績ベスト30</h4>\n", | |
"<p><img src=\"https://ryota-trade.com/wp-content/uploads/2018/04/WS000459.jpg\" alt=\"\" width=\"781\" height=\"551\" class=\"alignnone size-full wp-image-2485\" srcset=\"https://ryota-trade.com/wp-content/uploads/2018/04/WS000459.jpg 781w, https://ryota-trade.com/wp-content/uploads/2018/04/WS000459-300x212.jpg 300w, https://ryota-trade.com/wp-content/uploads/2018/04/WS000459-768x542.jpg 768w\" sizes=\"(max-width: 781px) 100vw, 781px\" /></p>\n", | |
"<p>上記のコードではトレード回数が100回に満たなかった結果データを全て捨てているので、どのパラメーターの組み合わせも最低限のサンプル数を確保できています。上位30の成績のパラメーターをみると、ほとんどが2時間足以上の時間軸なのがわかります。</p>\n", | |
"<h4>▽ PFで並び替え後の成績ワースト30</h4>\n", | |
"<p><img src=\"https://ryota-trade.com/wp-content/uploads/2018/04/WS000460-1.jpg\" alt=\"\" width=\"767\" height=\"549\" class=\"alignnone size-full wp-image-2495\" srcset=\"https://ryota-trade.com/wp-content/uploads/2018/04/WS000460-1.jpg 767w, https://ryota-trade.com/wp-content/uploads/2018/04/WS000460-1-300x215.jpg 300w\" sizes=\"(max-width: 767px) 100vw, 767px\" /></p>\n", | |
"<p>逆に最もプロフィットファクターの低かった(悪かった成績)30コをみると、短い時間軸(30分足)に集中しているのがわかります。ドンチアンブレイクアウトは、あまり短い時間軸には適さない戦略の可能性がありそうです。</p>\n", | |
"<p>(ただし時間軸によってテストに使用しているローソク足の期間が違うので、厳密には比較できない点に注意してください。)</p>\n", | |
"<h3>実行結果2</h3>\n", | |
"<p>次はさらに範囲を絞ってテストをしてみましょう!</p>\n", | |
"<p>今度は時間軸を1時間足・2時間足だけに絞り、上値ブレイクアウトの期間・下値ブレイクアウトの期間を10~55期間の範囲にして1期間刻みでテストしています。以下のようにパラメーターを設定すればOKですね。</p>\n", | |
"<pre class=\"prettyprint\">\n", | |
"# バックテストのパラメーター設定\n", | |
"\n", | |
"chart_sec_list = [ 3600,7200 ]\n", | |
"buy_term_list = np.arange(10,56) # 10~55までの連番の配列を作る\n", | |
"sell_term_list = np.arange(10,56)\n", | |
"\n", | |
"</pre>\n", | |
"<p>この条件でテストしてみると以下のようなCSVファイルが出力されます。</p>\n", | |
"<p><a href=\"https://ryota-trade.com/wp-content/uploads/csv/result-2018-04-23-10-11.csv\" rel=\"noopener\" target=\"_blank\">・CSV結果出力ファイル1</a><br />\n", | |
"<a href=\"https://ryota-trade.com/wp-content/uploads/csv/result-2018-04-23-11-12.csv\" rel=\"noopener\" target=\"_blank\">・CSV結果出力ファイル2</a></p>\n", | |
"<p>ファイル1は先ほどと同じくトレード回数が100回に満たなかったテスト結果を削除したもの、ファイル2は削除せずに全ての結果を残したものです。今回はファイル2を使ってみましょう。</p>\n", | |
"<h4>▽ PFで並び替え後の成績ベスト30(1時間足)</h4>\n", | |
"<p><img src=\"https://ryota-trade.com/wp-content/uploads/2018/04/WS000465-1.jpg\" alt=\"\" width=\"814\" height=\"703\" class=\"alignnone size-full wp-image-2558\" srcset=\"https://ryota-trade.com/wp-content/uploads/2018/04/WS000465-1.jpg 814w, https://ryota-trade.com/wp-content/uploads/2018/04/WS000465-1-300x259.jpg 300w, https://ryota-trade.com/wp-content/uploads/2018/04/WS000465-1-768x663.jpg 768w\" sizes=\"(max-width: 814px) 100vw, 814px\" /></p>\n", | |
"<p>1時間足の上位の成績を見ると、明らかに上値ブレイクアウト(買い)の期間は40前後、下値ブレイクアウト(売り)の期間は20前後が良さそう、という傾向が見えます。また判定基準は「終値」が上位を独占していますね。</p>\n", | |
"<h4>▽ PFで並び替え後の成績ベスト30(2時間足)</h4>\n", | |
"<p><img src=\"https://ryota-trade.com/wp-content/uploads/2018/04/WS000466.jpg\" alt=\"\" width=\"781\" height=\"698\" class=\"alignnone size-full wp-image-2561\" srcset=\"https://ryota-trade.com/wp-content/uploads/2018/04/WS000466.jpg 781w, https://ryota-trade.com/wp-content/uploads/2018/04/WS000466-300x268.jpg 300w, https://ryota-trade.com/wp-content/uploads/2018/04/WS000466-768x686.jpg 768w\" sizes=\"(max-width: 781px) 100vw, 781px\" /></p>\n", | |
"<p>2時間足の上位の成績を見ると、上値ブレイクアウト(買い)の期間は50以上、下値ブレイクアウト(売り)の期間は20~30期間がいいようですね。またやはり「終値」でブレイクアウトの判定をした方が、トレード回数は減るものの成績が良くなる傾向にありそうです。</p>\n", | |
"<p>より詳しく分析したい方は、上位のパラメーターを前回までの記事で学習した「<a href=\"https://ryota-trade.com/?p=2113\">月別集計</a>」「<a href=\"https://ryota-trade.com/?p=2023\">資産曲線</a>」などを使って調べてみるといいでしょう。</p>\n", | |
"<h3>Pythonコードの解説</h3>\n", | |
"<p>さて、それでは新しく変更した部分のpythonコードを解説しておきましょう!</p>\n", | |
"<p>基本的なコードは前回まで勉強した内容とほとんど変わっていないことに気付いたと思います。要するに、今まで作成してきた「While文でローソク足を回してバックテストし、最終成績をbacktest() で集計するコード」を、まるまる上記で説明した「総当たりのfor文」の中に入れるだけですね。</p>\n", | |
"<p>図にすると以下のような感じです。</p>\n", | |
"<p><img src=\"https://ryota-trade.com/wp-content/uploads/2018/04/param_seek_kouzou_zu.jpg\" alt=\"⇒ 各パラメーターの組み合わせでループする ⇒ ローソク足のデータを1本ずつループする ⇒ 全てのトレード成績を配列に記録する ⇒ トレード結果の配列を集計してpandasで表にする ⇒ 各バックテストの結果を計算して配列に記録する⇒ バックテスト結果の配列を集計してpandasで表にする ⇒ 最終結果の表をCSVに出力する\" width=\"640\" height=\"1096\" class=\"alignnone size-full wp-image-2585\" srcset=\"https://ryota-trade.com/wp-content/uploads/2018/04/param_seek_kouzou_zu.jpg 640w, https://ryota-trade.com/wp-content/uploads/2018/04/param_seek_kouzou_zu-175x300.jpg 175w, https://ryota-trade.com/wp-content/uploads/2018/04/param_seek_kouzou_zu-598x1024.jpg 598w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n", | |
"<p>こののような入れ子構造になっている点だけ混乱しなければ、上記のコードで難しい箇所はなかったと思います。</p>\n", | |
"<p>今までの記事では、バックテスト集計用の関数 backtest() は、単に集計・計算した結果を表示(print)するだけでしたが、今回のパラメーター最適化のコードでは、各バックテストの結果を返すように変更する必要があります。</p>\n", | |
"<p>そのためバックテスト集計用の関数に以下の部分を足しています。</p>\n", | |
"<pre class=\"prettyprint\">\n", | |
"# バックテストの計算結果を返す\n", | |
"result = {\n", | |
"\t\"トレード回数\" : len(records),\n", | |
"\t\"勝率\" : round(len(records[records.Profit>0]) / len(records) * 100,1),\n", | |
"\t\"平均リターン\" : round(records.Rate.mean(),2),\n", | |
"\t\"最大ドローダウン\" : -1 * records.Drawdown.max(),\n", | |
"\t\"最終損益\" : records.Profit.sum(),\n", | |
"\t\"プロフィットファクタ―\" : round( -1 * (records[records.Profit>0].Profit.sum() / records[records.Profit<0].Profit.sum()) ,2)\n", | |
"}\n", | |
"\n", | |
"return result\n", | |
"</pre>\n", | |
"<p>各バックテストの結果を配列に記録して、それを最後にpandasで1つの表にする方法は、前回までの記事の「各トレード結果を配列に記録して最後に集計する方法」と全く同じなので、説明は省きます。</p>\n", | |
"<h3>drop() で不要な行を削除する</h3>\n", | |
"<p>唯一新しく登場したのは、最後のdrop()の箇所でしょう。</p>\n", | |
"<p>今回のコードでは、トレード回数の少ないパラメーターの組み合わせを除外できるようにしています。それが以下の部分のコードです。</p>\n", | |
"<pre class=\"prettyprint\">\n", | |
"# トレード回数が100に満たない記録は消す\n", | |
"df.drop( df[ df[\"トレード回数\"] < 100].index, inplace=True )\n", | |
"</pre>\n", | |
"<p>表名.drop()は、行のindexを指定することで、その行を表から削除することのできる関数です。これを使って以下のような仕組みで、トレード回数の少なすぎるバックテスト結果を削除しています。</p>\n", | |
"<pre class=\"prettyprint\">\n", | |
"\n", | |
"# トレード回数が100未満のデータを抽出\n", | |
"df[df[\"トレード回数\"] < 100]\n", | |
"\n", | |
"# トレード回数が100未満のデータの行のindexを取得\n", | |
"df[df[\"トレード回数\"] < 100].index\n", | |
"\n", | |
"# トレード回数が100未満のデータを行ごと削除\n", | |
"df.drop( df[df[\"トレード回数\"] < 100].index )\n", | |
"\n", | |
"</pre>\n", | |
"<p>なお、inplce=Trueは、現在の表データに結果をそのまま上書きする、という意味です。以上でコードの解説はおしまいです!</p>\n", | |
"<h3>まとめ</h3>\n", | |
"<p>この章の前の方の記事でも解説したように、このようなパラメーター調整には常にカーブフィッティングの問題がつきまといます。以下のような点に注意しておきましょう。</p>\n", | |
"<p>(1)パラメーターの数を増やし過ぎないようにする<br />\n", | |
"(2)おおまかな傾向を捉えることを目的にする<br />\n", | |
"(3)トレード数(サンプル)が少なくならないようにする</p>\n", | |
"<p>一般論としていえば、今回のドンチャン・チャネル・ブレイクアウトのように広範なパラメーターのどの値を使ってもプラスの期待値が出る、という場合で、おおまかな傾向を捉える目的(例えば、20期間あたりが一番良さそう)でパラメーターを最適化するのは問題ありません。</p>\n", | |
"<p>一番問題があるのは、例えば、「6」という数字を使えば利益が出る、「8」という数字を使うとマイナスに転落する、「13」という数字を使えばプラスになる、といった全く連続性も傾向もないパラメーターを恣意的に調整することです。これをすると、過去データでしか利益の出せないパラメーター調整になります。</p>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [] | |
} | |
], | |
"metadata": { | |
"hide_input": false, | |
"kernelspec": { | |
"display_name": "Python 3", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.7.5" | |
}, | |
"latex_envs": { | |
"LaTeX_envs_menu_present": true, | |
"autoclose": false, | |
"autocomplete": true, | |
"bibliofile": "biblio.bib", | |
"cite_by": "apalike", | |
"current_citInitial": 1, | |
"eqLabelWithNumbers": true, | |
"eqNumInitial": 1, | |
"hotkeys": { | |
"equation": "Ctrl-E", | |
"itemize": "Ctrl-I" | |
}, | |
"labels_anchors": false, | |
"latex_user_defs": false, | |
"report_style_numbering": false, | |
"user_envs_cfg": false | |
}, | |
"nbTranslate": { | |
"displayLangs": [ | |
"*" | |
], | |
"hotkey": "alt-t", | |
"langInMainMenu": true, | |
"sourceLang": "en", | |
"targetLang": "fr", | |
"useGoogleTranslate": true | |
}, | |
"toc": { | |
"base_numbering": 1, | |
"nav_menu": {}, | |
"number_sections": true, | |
"sideBar": true, | |
"skip_h1_title": false, | |
"title_cell": "Table of Contents", | |
"title_sidebar": "Contents", | |
"toc_cell": false, | |
"toc_position": {}, | |
"toc_section_display": true, | |
"toc_window_display": false | |
}, | |
"varInspector": { | |
"cols": { | |
"lenName": 16, | |
"lenType": 16, | |
"lenVar": 40 | |
}, | |
"kernels_config": { | |
"python": { | |
"delete_cmd_postfix": "", | |
"delete_cmd_prefix": "del ", | |
"library": "var_list.py", | |
"varRefreshCmd": "print(var_dic_list())" | |
}, | |
"r": { | |
"delete_cmd_postfix": ") ", | |
"delete_cmd_prefix": "rm(", | |
"library": "var_list.r", | |
"varRefreshCmd": "cat(var_dic_list()) " | |
} | |
}, | |
"types_to_exclude": [ | |
"module", | |
"function", | |
"builtin_function_or_method", | |
"instance", | |
"_Feature" | |
], | |
"window_display": false | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment