tradingviewの公開されたstrategyを解読してみた(Pivot Reversal Strategy編)

mint(@mint_gamu_)です。

tradingviewで公開されているstrategy「Pivot Reversal Strategy」のコードを一行ずつ解読して、エントリーロジックはどうなってるのか考察してみました。

実際のコードがこちらです。

◆1〜2行目

strategyの最初のおまじない。

◆4〜5行目

input(数字)はユーザーが設定する値を取得します。数字はデフォルトの値を意味し、leftBarsはデフォルトで4が設定されていて、rightBarsは2がデフォルトで設定されています。

◆7〜8行目

tradingviewではpivothighとpivotlowという公開APIがあります。詳細は下記のtradingview公式サイトにあるAPIリファレンスのpivothighが参考になります。

pivothigh:この関数はピボットハイポイントの価格を返します。 ピボットハイポイントがない場合は'NaN'を返します。 引用元: PINEスクリプト言語リファレンスマニュアル

そもそも、pivothighはどういう考え方なのでしょうか?pivotで検索するとJ・ウェルズ・ワイルダー氏が作成したインジケーターが出てますが、結論をまず述べると、tradingivewのpivothighとワイルダー氏のpivotとは全く別物です。

◆J・ウェルズ・ワイルダー氏のpivotとは?

Pivotは前日の値動きから当日の値動きの範囲を予測しようというものなります。値動きの範囲は、下記画像のようなレジスタンスラインとサポートラインで表現されます。

◆tradingviewのpivothighのpivotは?

tradingviewのpivothighは、引数で左のバーの数と右のバーの数が指定して、戻り値としては高値もしくはNaNを返します。このことからも分かる通り、前日の値動きから当日の値動きの幅を予想するものとは別物です。私は最初J・ウェルズ・ワイルダー氏のpivotと勘違いしていて、pythonでbot化する時に戸惑いました。。

それでは左のバーの数と右のバーの数を指定してpivothighは何を戻り値として返しているのでしょうか?

その答えはとても単純でシンプルです。つまり、あるロウソク足の高値が、左のロウソク足と右のロウソク足の高値より高いかどうか見比べて、高ければその価格を返すというものになります。左のロウソク足は、過去のロウソク足を意味し、右のロウソク足は将来のロウソク足を意味します。つまり、引数で過去と将来の期間を指定して、現在のロウソク足が高いかどうか見ているだけなのです。

こんな単純なロジックで何が分かるかといえば、指定された期間の中で、価格が突出しているところ(山or谷)が見抜けるようになります。

このpivotの考え方は、下記のEasyLanguage研究所のピボットハイ/ローの説明がとても参考になるので下記引用します。

ある足の高値(安値)が、その左右にある一定本数の足すべての価格(通常は高値/安値)よりも高い(安い)時、サインを表示します。ちょうど、価格の「山」「谷」のようなとがった部分にサインが表示されます。引用元: ピボットハイ/ピボットロー - EasyLanguage研究所

7~8行目の説明に戻ると、swhには、引数で指定された期間で、現在のロウソク足が高値更新していれば、その高値が返されます。そうでない場合はNaNが返されます。swlには、その反対に安値が返されます。ちなみに、デフォルトでは過去のロウソク足の数は4、将来のロウソク足の数は2が指定されているようです。

◆10行目

na(変数)は、変数がNaNであれば、trueを返します。つまり、高値を返していれば、NaNではないのでfalseを返します。それに加えて not が付与されていることから、swh_condには高値が入っていればtrueが格納されます。

◆12〜13行目

hpriceを初期値0.0で初期化してから、swh_condがtrueの場合には、swhを格納します。つまり高値更新時には、高値が代入されます。そうでない場合は、hprice[1]を代入します。[1]は前回の計算時の値が代入されます。

◆15〜16行目

leを初期値falseで初期化してから、swh_condがtrueの場合には、trueを格納します。そうでない場合は、le[1]がtrueで且つhigh(現在の高値)がhpriceよりも高い場合に、falseを格納し、そうでない場合にはle[1]を格納します。

三項演算子が入れ子になっていて分かりづらいですね。。

前回が高値更新ではなかったときには、le[1]には当然、falseが入るので、入れ子の中の三項演算子がtrueになっても、leはfalseが代入されます。

それでは入れ子の中の三項演算子のロジックが動いて最終的にleがtrueになるのはどういう時なのでしょうか?

それは、前回が高値更新でtrueのときです。前回が高値更新でtrueのときには、自ずとle[1]にはtrueが代入されています。そのときに、現在の高値がhprice(これには13行目によりswhではなくhpric[1]が代入されています)と比べて高くないときにtrueになります。

ここがこのstrategyの一番難しくて落とし穴になるところのように思います。つまり、現在のロウソク足算出時には指定された期間で高値ではないけれども、前回のロウソク足算出時には指定された期間で高値を更新していて、その前回の高値よりも現在の高値が低い場合にはleはtrueということです。

この後、leがtrueの場合はロングエントリーを入れるわけですが、この三項演算子の入れ子のtrueがはいると、前回に引き続きleがtrueとなり、2回連続でロングエントリーが入れられることなります。ここで何が起こるかといえば、tradingviewのバックテストの落とし穴と言われるrepaintingが発動します(笑)この結果、何が起こるかといえば、バックテストの結果がよくなります。これをrepaintしないようにするにはどうすればいいかといえば、単純にこの三項演算子によるtrueをなくせばいいということになります。このrepaintingを引き起こす16行目を書き直すとこうなります。

le := swh_cond ? true : false

これにより、現在のロウソク足が高値更新でなければfalseでleが連続でtrueになるのを防ぐことができます。

ただその背反として、バックテストの結果は変わります(笑)

◆公開されているstrategyのバックテスト結果(改善前)

◆改善後のバックテストの結果

最大ドローダウンが23.3%から47.39%に変わっています。これはつまり再描画によりもっといいポイントでロングエントリーし直して負けを抑えられたと言い換えることができると思います。。

◆18行目〜19行目

ここでleがtrueの場合にはロングエントリーを入れます。

◆21行目〜31行目

12行目〜19行目で説明したもののショート版になります。

◆おわりに

以上、Pivot Reversal Strategyのコード解読でした。他のstrategyを色々解読したわけではないので一概に一般化することはできませんが、エントリーの判断ロジックに三項演算子の入れ子が入っていたりして且つ過去のデータも参照して複雑化されている場合はrepaintingを疑ったほうがいいかもしれません。自分の理解の確認のために文章化しましたが他の人のbot作成の参考にもなれば幸いです。

この記事が気に入ったらサポートをしてみませんか?