本日は初心者向け講座となる。
まずは前回のラベルを思い出しながら次のコードを実行してみよう。
【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
である。
var
var
を頭につけて変数を宣言すると
「初めの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.1
float
という宣言を併用するか、初めから少数を入れておく。
どうやら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で使いやすくなったので、初心者も積極的に使っていこう。