2020-06-13

【Pine Script】Pineの「変数」はクセが強い。

プログラミングでは超基本の「変数」。Pineではちょっとクセが強い。

Article Image

Pineの変数はクセが強い。

本日は初心者向け講座となる。

まずは前回のラベルを思い出しながら次のコードを実行してみよう。

【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」という数字をバーの上に描画するだけ。

実行結果

image-20200612225829487

期待通りの結果。

ちなみにtostroing(n)となっているが、これは数値を文字列に変換するコード。

labelに表示する変数は文字列でないといけないので変換が必要。覚えておこう。

n = n + 1

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 )

こうなのだ。

実行結果

image-20200612230223586

えっ?

そう。えっ?なのだ。

これが最初のPineのクセ。

n1のまま。

なんで変数が変化しないのか

Pineは宣言されている変数はバーが変わる度にコードが上から再実行されると考えておく。

つまりバーが新しくなるたび n = 0 となるため n+1しても1のままなのだ。

nを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 )

実行結果

image-20200612230834858

想像通り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という宣言を併用するか、初めから少数を入れておく。

実行結果

image-20200612232531994

どうやらfloatには誤差があるようで、注意だ。

誤差を補正

意図しない値になってしまう場合は切り上げのceilや四捨五入のround、切り捨てのfloorをうまく使おう。

プログラミング言語によってこの関数はそれぞれだが、少数点以下の演算では内部の微妙な都合によりほんの僅かな誤差がでるのはプログラミングでは一般的。知らなかったら覚えておこう。

ceilを使う場合

ceil(x)はxを整数として小数点以下を切り捨てる

つまりこの例題に入れてしまうと

var float n = 0
n := ceil(n + 0.1)

image-20200612233004772

整数になってしまう。

0.1ずつ足したい

ceil(x)xは整数なので(round他も整数)0.1より下位の少数を消したいのであれば

  • 10倍する
  • ceilで小数点以下を切り捨てる
  • 10で割る

という手順を踏む。

コードは

var float n = 0
n := ceil( (n + 0.1) * 10) / 10

image-20200612233258198

誤差が補正された。

面倒ではあるが私が普段メインにしている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以降しかトレードしないようにしている。

実行結果

image-20200612235038756

青の矢印がロング

赤がショート

紫がクローズ である。

posという変数に現在のポジションが保存されているため、正しく順番に取引を切り替えられているのが分かる。

おまけ

image-20200612235240846

なぜかこのストラテジ、結構強い。

ストラテジーテスターを押すとバックテストが見れるのでやってみよう。

image-20200612235419479

余談:Square Position

ノーポジは和製英語だ。ノーポジは英語ではSquare Positionという(厳密には買いと売りは持っているが同量で相殺されている状態を指すと思われる)

参考サイト(英語):Square Position

これが日本語で調べてもロングやショートは出てくるのだが、ノーポジの英語が全然ヒットしないので私がドヤ顔でこの余談を書いておく。

さいごに

このように変数は内部でポジションを記憶しておくには非常に便利なものとなる。

一般のプログラムと一緒で整数や少数、文字列等を保存しておけるのでアイデア次第で色々可能。

V4で使いやすくなったので、初心者も積極的に使っていこう。



この記事のタグ

この記事をシェア


謎の技術研究部 (謎技研)