このブログも本格的に書き始めてそろそろ1年が経とうとしています。その間、訪れていただく方はどんどん増えており感激する毎日です。また、コメントいただく機会も増えてきたため、いろいろな質問をいただく機会も増えました。
そこで、今回は読者さんからご質問をいただいた「複数条件がそろった際にエントリーするストラテジーをどのように組むか」について解説していきます。
おそらく、質問の意図としては「一つの足で同時に条件を満たす場合ではなく、段階的に条件を満たした場合にエントリーするロジックを組みたい」ということだと思うので、その内容について実際のストラテジーとコードを追いながら説明していきます。
1,基本的な考え方について
まずは、複数条件がそろった際にエントリーするストラテジーの基本的な考え方について解説します。
早速ですが、そのようなストラテジーを作成したい場合は以下のフレームワークにあてはめれば大抵の場合問題なく作成することができます。
以下のフレームワークでは条件の数が2つの場合を考えています。
見ていただくと分かるように、大きく4つの部分から成り立っています。大まかな流れとしては、条件の数だけブール値を格納する変数を用意し条件が成り立った場合にtrueを代入、そしてすべての条件が満たされた場合にエントリーをし、最後に適当なタイミングでポジションクローズするという流れとなっています。
ここで、こんな疑問を持った方はいないでしょうか?
「そもそも、ブール値を格納する変数なんて必要なくて以下のようにすればいいんじゃない?」
つまり、こんな感じ
シンプルに、3つ目の「条件を満たした場合にエントリーする」部分のみで同じ動作を得られるのではないかと思うということです。
しかし、ここが今回の記事のポイントです!
実は、上記のようなコードを書くと条件①と条件②が「同時に」trueとならないとエントリーされません。
しかし、今回は同時という条件は必要なくて時間の流れとともにすべてがtrueとなったときにエントリーするストラテジーなので、このように記述すると誤りとなります。
例えば、ta.crossover関数などはクロスした直後にしかtrueとなりません。そのため、複数条件が同時にtrueとならない場合はエントリーしなくなります。
それを防ぐためにこのフレームワークのようなコードとする必要があるということです。
2,図で表すとこんな感じ
この後からの説明をやりやすくするため処理の流れを図で表しました。
この図の緑のボールが処理、凹の部分が条件①、②を表しており、エントリーのところにボールが到達するとエントリーを行うという図です。いわゆる、ピタ〇ラスイッチ的な奴です。
条件がそろっていないと処理は途中で落ちてしまいエントリーしません。
しかし、条件が満たされtrueブロックが出てくることにより凹が埋まり処理が続行します。
最終的にすべての条件が満たされた場合にのみエントリーのかごまで処理が届き、エントリーするというイメージです。
また、エントリーした場合はリセットバーが外れることにより、すべてのtrueボックスが落ちることで、初期状態に戻ります。
このイメージをコードに落としていくだけです。
3,フレームワークの説明
では、これらをもとにフレームワークの各部について説明をします。
(1)ブール値を格納する変数を用意する部分
ここでは、希望する条件の数だけ変数を用意します。
今回は例として2つの条件を満たした場合にエントリーするロジックを考えますので、ここでは2つ用意しています。
図でいうと、凹の部分が変数に当たります。
(2)条件を満たした場合にブール値を変える部分
次に、条件を満たした場合にブール値内の値をtrueにします。
先ほどの図でいえば、変数の凹にtrueのブロックを入れる作業に当たります。
ここで、2つの条件が「条件① and 条件②」となっています。
これは、今回作成するロジックが順序を含め条件を満たした場合にエントリーするロジックであることに起因します。
もっとわかりやすく言えば、条件①を満たした後に条件②を満たしどちらの条件もtrueである場合にエントリーするロジックということです。図で説明するとこんな感じ↓
条件①が埋まってないのに条件②にtrueブロックを埋めることは許さないということです。
仮に「順序はどうでもよくて、例えば条件②が満たされた後に条件①を満たした場合でもエントリーしてよい」というロジック(図でいう右のロジック)であれば、この箇所はシンプルに
if 条件②
bool_2 = true
と記述して問題ありません。
(3)条件を満たした場合にエントリーする&ブール値の変数をリセットする部分
次にこの部分です。
シンプルに条件①と条件②が満たされた場合にエントリーする処理になります。
図でいえば、両方変数の凹にtrueブロックが埋まっており処理がエントリーのかごまで達した場合に当たります。
また、以前の記事でも解説しましたが、Pineスクリプトでは一つの足ごとに処理が実施されます。
つまり、図でいえば緑のボールが毎足落ちてくる形になるため、trueブロックをそのままにしておくと毎足ごとにエントリーしてしまいます。(厳密にいえばstrategy.entry関数はピラミッディングしないため問題ありませんが、strategy.order関数などであれば何回でもエントリーしてしまいます)
そのため、エントリーした直後に変数をリセットしておかないといけません。それを実施しているのが
bool_1 = false
bool_2 = false
の部分になります。
こうすることで、次の足で処理が実行されてもエントリーできないような仕組みにしています。
(4)ポジションをクローズする部分
あとは、適当なタイミングでポジションをクローズしてしまいましょう。
ここに関してはいつも通りにポジションをクローズすればいいだけなので説明は割愛します。
4,実際にストラテジーを作成してみる
では、これらの考え方を踏まえたうえで実際に以下のようなストラテジーを作ってみましょう。
今回は以下のような簡単なストラテジーを作成します。
【エントリーする条件】
1,2本の移動平均線がデッドクロスする
2,デッドクロスした後につつみ線が出てきたらロングエントリー
「そんなストラテジー儲かるの?」とかいう疑問はなしでお願いします。今回はあくまでも例として考えただけです。
そして、実際に作成したコードは以下の通りです。
//@version=5
strategy(“B_1″, overlay=true)
short_width = input(title=”短期移動平均”,defval = 100)
long_width = input(title=”長期移動平均”, defval = 300)
s_ema = ta.ema(close,short_width)
l_ema = ta.ema(close,long_width)
plot(s_ema,”短期移動平均線”,linewidth=1,color = color.red)
plot(l_ema,”長期移動平均線”,linewidth=1,color = color.blue)
//エントリー条件に使用するブール値を格納するための変数(条件の個数によって用意する数を変化)
var bool_1 = false
var bool_2= false
//bool_1をTrueにするための条件
if ta.crossunder(s_ema,l_ema)
bool_1 := true
//bool_2をTrueにするための条件
if bool_1 and (high-high[1]) > 0 and (low-low[1]) < 0
bool_2 := true
if bool_1 and bool_2
strategy.entry(“Long”,strategy.long)
//すべてのブール値をリセットする
bool_1 := false
bool_2 := false
//クローズする条件
strategy.close(“Long”,ta.crossover(s_ema,l_ema))
早速解説していきます。
(0)移動平均線を作成して表示させる部分
strategy(“B_1″, overlay=true)
short_width = input(title=”短期移動平均”,defval = 100)
long_width = input(title=”長期移動平均”, defval = 300)
s_ema = ta.ema(close,short_width)
l_ema = ta.ema(close,long_width)
plot(s_ema,”短期移動平均線”,linewidth=1,color = color.red)
plot(l_ema,”長期移動平均線”,linewidth=1,color = color.blue)
ここは今回の記事の趣旨から外れるので0番としました。今回のストラテジーでは移動平均線が必要なのでそれを2本用意し、わかりやすくするためにplot関数で表示しているだけですね。
(1)ブール値を格納する変数を用意する部分
var bool_1 = false
var bool_2= false
ここで、2つの変数をブール値を格納するための変数として用意しています。
初期値としてfalseを代入しておいて、条件を満たした場合にtrueが入るようにします。
(2)条件を満たした場合にブール値を変える部分
//bool_1をTrueにするための条件
if ta.crossunder(s_ema,l_ema)
bool_1 := true
//bool_2をTrueにするための条件
if bool_1 and (high-high[1]) > 0 and (low-low[1]) < 0
bool_2 := true
ここで、s_emaがl_emaを下抜けした場合にbool_1にtrueが代入されるようにif文を入れています。
そして、さらにbool_1がtrueとなっていてかつ、つつみ線が出現した場合にbool_2にtrueが入るようにif文を入れています。
なお、今回のつつみ線は前回の足より最高値が高く、最安値が低いものという条件にしました。
以下の図の右側にある陰線のイメージです。
(3)条件を満たした場合にエントリーする&ブール値の変数をリセットする部分
if bool_1 and bool_2
strategy.entry(“Long”,strategy.long)
//すべてのブール値をリセットする
bool_1 := false
bool_2 := false
続いて条件を満たした場合にエントリーする部分と、ブール値のリセットを行う部分です。
strategy.entryでエントリーを行い、それが終わったらブール値をリセットしているだけです。
ここは特別なことは何もありません
(4)ポジションをクローズする部分
//クローズする条件
strategy.close(“Long”,ta.crossover(s_ema,l_ema))
最後にポジションをクローズする箇所です。ここも特別なことは何もありません。
シンプルにポジションをクローズしているだけになります。
5,おまけ
今回作成したストラテジーを先日リリースしたOptimaHeatを使用して最適なパラメーターを求めようと思います。今回は30分足での最大利益を求めてみようと思います。
以下が結果になります。
【BTCUSD】
【ETHUSD】
とにかくこのままでは使えなさそうなストラテジーであることがわかりますが、それよりもETHUSDでの成績が悪すぎる!
やはり、BTCとETHは値動きの特徴が違うのでしょうね。
なぜこのような違いが出るのかを追い求めれば利益率を上げることが可能かもしれませんね。
6,まとめ
複数条件でエントリーするロジックを作成する場合は、
1,条件の数だけブール値を格納するための変数を用意する
2,条件がそろい次第、各変数にtrueを代入していく
3,すべての条件が満たされた場合にエントリーするようにif文を設置し、同時にすべての変数のブール値をリセットする
を意識して作成してください。
最初にあげたフレームワーク通りに作成すれば問題ありません。
では、よきPineスクリプトライフを~
それではまた。
本記事を気に入っていただけたらブックマークお願いします!また、ツイッターもやってるのでフォローよろしくお願いいたします!
Twitter : makoto(@Makoto_beginner)
また、記事中で不明なことや間違い等ありましたら以下のコメント欄からコメントいただけると幸いです。
皆様のコメントをもとにどこよりもわかりやすいブログを目指していきます。