この記事執筆時点ではむとれのストラテジ開発大会は終了しているが、最後の滑り込みで投稿したストラテジを紹介する。
XRP
でやってみたいところだったが、どうも良いストラテジが作成できなかったのでとりあえず形になったETH
のストラテジを深堀りした。
ByBit
のETH
で取引を想定している。
長めのヒゲがでたということはヒゲが出た方向への成り行きは押し負けている(そのラインで反発したと見る)
つまり上髭が出た時にショート。下ヒゲが出た時にロング
ヒゲ自体は頻繁に観測されるもの。
どうやって「このヒゲは長い」を判断するのか。
こんなコードにした。
upper_shadow = open > close ? high - open : high - close //上ヒゲ
lower_shadow = open > close ? close - low : open - low //下ヒゲ
この構文わからない人もいるかも知れないので軽く説明。
変数 = 条件 ? trueなら ここの値をreturn : falseならここの値をreturn
ちなみにJavaScript
でもこの構文は全く同様に作用する。
つまり上髭の検出は
open > close
か?high - open
を upper_shadow
に設定high - close
をupper_shadow
に設定どうだろうか。上髭の定義ではないだろうか。
はい!カンが鋭い方も、鈍い方もお気づきである。
「標準化」である。もう聞き飽きたかもしれない。
標準化したデータの平均は0に、分散(標準偏差も)は1になります。これにより、異なる項目のデータであってもその大小を比較できるようになります。すなわち、大きければ大きいほど成績が良いことを表します。
6-2. データを標準化してみよう | 統計学の時間 | 統計WEB より引用
こうすることにより「絶対値で上ヒゲより下ヒゲのほうが長いクセがあるような相場」でも「上ヒゲと下ヒゲの長さ比較が可能」となる。
ちょっと何言っているかわからないかもしれないが...あまり難しく考える必要はない。
とにかくバランスが悪くても上ヒゲと下ヒゲを比較できるのだ。
今回はこれにより現在のバーの「上ヒゲと下ヒゲどちらが長いか」をオフセットを加えた値以上に長くなったかどうかでエントリーを判断している。
残念なこととしては、現在のバーだけで髭の長さを判断している。
本来なら過去のバーを元に何らかの分析を行った数値から今の髭が長いかどうかを判断すべきかと思うが、価格と違い髭はそのバーごとに独立していると考えられるため長さのma
などは意味がないと考えた。
結局アイデアが浮かばなかった。
そのためか、これだけでエントリーするとヒゲそのものがトレンドを確定するには情報が不十分だった。
いままで作成したVolumeのZ値(標準化した数値)が小さいときはエントリーしないというフィルタとCLOSEのZ値を利用してトレンドを判定する方法を組み合わせている。
結局の所ヒゲというよりこちらの影響のほうが大きい可能性が高いので正直今回のストラテジアイデアはイマイチかもしれない。
次のバックテストを見ると
底でショートしている。
よくみると赤い矢印が書いてある箇所の2つ前のバーで大きな下ヒゲがでているのでロングを取りたいところだが、これは「上昇トレンド中の急反発」でありClose
のZ値よりロングが制限されてしまった。
加えて赤い矢印の一つ前の足で長い上ヒゲが出ている。
丁度このバーで上昇トレンド判定が下降トレンドへ転換したため運悪く下降トレンド中の上ヒゲでショートが入ってしまう。
(とは言え上にヒゲが出ているのは確かなのでもう1段下げるという可能性もあるにはあった)
そのため逆張りで稼ぐというタイプではなくなってしまった。
以前の記事「【Pine Script】Pineの「変数」はクセが強い。」のコードを役に立てる。
応用編で解説したPineの変数を使ってポジション管理をするというコードだ。
今回はこれを使ってロング時とショート時で条件を反転する「損切/利確」ロジックを導入したので興味があればコードを覗いてみてほしい。
残念ながら損切りするよりホールドしたほうがPF的には良い結果となってしまったので、こちらはデフォルトでオフにしてある。
var pos = "square"
これでポジションを管理している。
ユーザーが設定できるのは以下
標準化の期間は設定が難しいのでデフォルトで良い
基本的にエントリー条件を調整するなら「長さの差」をいじる
一方的に逆行した時の爆損が怖いのであれば自動で損切りモードをオンにする(PFは下がる)
ByBit
のETHUSD
の30分足で調整。
決して良い値ではないが、私が作ってきたものの中ではシャープレシオが良い方。
今回は勝ってはいるが締切に間に合わせるために正直いちばん出来が悪いストラテジ(過去のフィルタを再利用)となった。
もうちょっとヒゲの扱いを考えたパターンを出せると思うので是非色々試してもらえたらと思う。
最後にコードを貼り付けて本日は終了とする。
//@version=4
//ByBit ETHUSD 30分足
strategy("髭鬚髯", overlay=false, default_qty_type=strategy.cash, initial_capital=100000, default_qty_value=100000,currency="USD")
n = input(type=input.integer,defval=200 ,title="標準化に使用する期間")
offset = input(2,step=0.1,title="エントリーするヒゲの長さの差")
volume_threshold = input(0,step=0.1,title="VolumeのZ値がこの値より低いときはエントリーしない")
songiri = input(false,title="自動で損切/利確")
songiri_threshold = input(2,step=0.1,title="損切/利確の閾値(通常は+の値)")
upper_shadow = open > close ? high - open : high - close
lower_shadow = open > close ? close - low : open - low
upper_shadow_sma = sma(upper_shadow, n)
upper_shadow_Zscore = ( upper_shadow - upper_shadow_sma ) / stdev(upper_shadow,n)
plot(upper_shadow_Zscore,color=color.blue)
lower_shadow_sma = sma(lower_shadow, n)
lower_shadow_Zscore = ( lower_shadow - lower_shadow_sma ) / stdev(lower_shadow,n)
plot(lower_shadow_Zscore,color=color.red)
close_sma = sma(close, n)
close_Zscore = ( close - close_sma ) / stdev(close,n) //標準化
plot(close_Zscore,color=color.white)
volume_sma = sma(volume, n)
volume_Zscore = ( volume - volume_sma ) / stdev(volume,n) //標準化
longCondition = lower_shadow_Zscore > upper_shadow_Zscore + offset and close_Zscore > 0 and volume_Zscore > volume_threshold
shortCondition = lower_shadow_Zscore + offset < upper_shadow_Zscore and close_Zscore < 0 and volume_Zscore > volume_threshold
var pos = "square"
if (longCondition)
strategy.entry("My Long Entry Id", strategy.long)
pos := "long"
else if (shortCondition)
strategy.entry("My Short Entry Id", strategy.short)
pos := "short"
else if(songiri and pos == "long" and close_Zscore < songiri_threshold * -1)
strategy.close_all()
pos := "square"
else if(songiri and pos == "short" and close_Zscore > songiri_threshold)
strategy.close_all()
pos := "square"