本日は初心者向け講座となる。
まずは前回のラベルを思い出しながら次のコードを実行してみよう。
【Pine Script】 ラベルオブジェクト(label)の使い方【Version 4】 | Blog
//@version=4
study("マイスクリプト", overlay=true)
n = 1
l = label.new( bar_index, na, text=tostring(n), yloc=yloc.abovebar )なにも特殊なことをしていない。
「1」という数字をバーの上に描画するだけ。
期待通りの結果。
ちなみにtostroing(n)となっているが、これは数値を文字列に変換するコード。
labelに表示する変数は文字列でないといけないので変換が必要。覚えておこう。
nを1ずつ足してみよう。
//@version=4
study("マイスクリプト", overlay=true)
n = 0
n = n + 1
l = label.new( bar_index, na, text=tostring(n), yloc=yloc.abovebar )これはコンパイルエラーになる。
line 7: 'n' is already defined.
nは既に定義されています。
Pineの最初のクセは変数の定義は=。代入は:=という演算子を使い分けねばならない。
つまり正しくは
//@version=4
study("マイスクリプト", overlay=true)
n = 0
n := n + 1 //ここの演算子に注目!
l = label.new( bar_index, na, text=tostring(n), yloc=yloc.abovebar )こうなのだ。
えっ?
そう。えっ?なのだ。
これが最初のPineのクセ。
nは1のまま。
Pineは宣言されている変数はバーが変わる度にコードが上から再実行されると考えておく。
つまりバーが新しくなるたび n = 0 となるため nは+1しても1のままなのだ。
v3までのPineであれば次のようにする必要があった。
//@version=4
study("マイスクリプト", overlay=true)
n = 0
n := nz(n[1])
n := n + 1
l = label.new( bar_index, na, text=tostring(n), yloc=yloc.abovebar )想像通り1ずつプラスされている。
まずn[1]
これは1コ前のバーのときのnの値だ。
例えば3番目のバーのときは0 1 2と入るのでn[0](現在のバーのn)は2
そしてn[1]は一つ前なので1が入っている。
つまり一つ前のバーの値を読み込んできてからそれに+1してやればnは増えていく想定だ。
ただ、次だと問題がある。
n = 0
n := n[1]
n := n + 1これだと一番最初のバーの処理で躓く。
一番最初のバーの時、その一つまえのバーが存在しないのでn[1]が未定義でありエラーとなってしまう。
そこでnz(n[1])の登場。
これはnz(値)の値がNaN値、つまりNullのように**「何も入っていなかった」**場合は強制的に0になるコードである。
これで問題なくバーに1ずつプラスされる番号が付与された。
たぶん上で言っている事が全然わからん人も多い。
このコードはv3以前で強引に再現していただけのコードでプログラミングとしてはスマートではない。
実はこの問題を公式が認めてV4にて次のコードが導入された。
//@version=4
study("マイスクリプト", overlay=true)
var n = 0 //ここ!!
n := n + 1
l = label.new( bar_index, na, text=tostring(n), yloc=yloc.abovebar )varである。
varvarを頭につけて変数を宣言すると
「初めの1回だけ」0が代入され
以降はvarの行は処理されなくなる。
実に簡単だ。
だが、次がエラーになる。
//@version=4
study("マイスクリプト", overlay=true)
var n = 0
n := n + 0.1 //ここに注目
l = label.new( bar_index, na, text=tostring(n), yloc=yloc.abovebar )エラー内容は
line 7: Variable 'n' was declared with 'integer' type. Cannot assign it expression of type 'const float'.
integerで定義されているのでfloatは代入できません。
少数を扱うときは明示する必要がある。
次の二通りでOK
var n = 0.0
n := n + 0.1もしくは
var float n = 0
n := n + 0.1floatという宣言を併用するか、初めから少数を入れておく。
どうやらfloatには誤差があるようで、注意だ。
意図しない値になってしまう場合は切り上げのceilや四捨五入のround、切り捨てのfloorをうまく使おう。
プログラミング言語によってこの関数はそれぞれだが、少数点以下の演算では内部の微妙な都合によりほんの僅かな誤差がでるのはプログラミングでは一般的。知らなかったら覚えておこう。
ceilを使う場合ceil(x)はxを整数として小数点以下を切り捨てる。
つまりこの例題に入れてしまうと
var float n = 0
n := ceil(n + 0.1)整数になってしまう。
0.1ずつ足したいceil(x)のxは整数なので(round他も整数)0.1より下位の少数を消したいのであれば
という手順を踏む。
コードは
var float n = 0
n := ceil( (n + 0.1) * 10) / 10誤差が補正された。
面倒ではあるが私が普段メインにしているJavaScriptでも基本的な考え方は同じだ。
さて、グラフ用ではなく自前で用意した変数を使っていきたいときにはどんな時があるだろうか。
私は真っ先に「ポジション管理」を思いつく。
はむとれではこのような方法でポジションを管理している。
ノーポジ → 買い → 売り → ノーポジを繰り返すプログラムを組んでみよう。
//@version=4
startDay = timestamp(2020,6,1,0,0)
strategy("マイスクリプト", overlay=true)
var pos = "SQUARE"
if(pos == "SQUARE" and time > startDay)
strategy.entry("long", strategy.long)
pos := "LONG"
else if(pos == "LONG")
strategy.entry("short", strategy.short)
pos := "SHORT"
else if(pos == "SHORT")
strategy.close_all()
pos := "SQUARE"※strategy.entryが成り行きでポジションを持つコード。チャート上取引回数が9000を超えるとエラーとなるのでtimestampを使って6/1以降しかトレードしないようにしている。
青の矢印がロング
赤がショート
紫がクローズ である。
posという変数に現在のポジションが保存されているため、正しく順番に取引を切り替えられているのが分かる。
なぜかこのストラテジ、結構強い。
ストラテジーテスターを押すとバックテストが見れるのでやってみよう。
ノーポジは和製英語だ。ノーポジは英語ではSquare Positionという(厳密には買いと売りは持っているが同量で相殺されている状態を指すと思われる)
参考サイト(英語):Square Position
これが日本語で調べてもロングやショートは出てくるのだが、ノーポジの英語が全然ヒットしないので私がドヤ顔でこの余談を書いておく。
このように変数は内部でポジションを記憶しておくには非常に便利なものとなる。
一般のプログラムと一緒で整数や少数、文字列等を保存しておけるのでアイデア次第で色々可能。
V4で使いやすくなったので、初心者も積極的に使っていこう。
