【Pine 基礎】Pine スクリプトの実行モデル

Pine スクリプトのランタイムは、コードがコンパイルされた後に起動し、スクリプト実行のトリガーとなるイベントが発生した場合にチャート上で実行される。

  • データセット (dataset) :とあるシンボル、時間足のチャートにおいて、読み込むことができるローソク足全てのことをデータセットと呼ぶ。
  • ヒストリカルバー (historical bars) :スクリプトが最初に実行されたとき、データセット内の閉じているローソク足のことをヒストリカルバーと呼ぶ。
  • リアルタイムバー (realtime bar) :シンボルが取引可能なときの最右端のバーのことをリアルタイムバーと呼ぶ。リアルタイムバーは価格や出来高に変化があると更新される。
  • 経過したリアルタイムバー (elapsed realtime bar) :閉じられたリアルタイムバーのこと。(リアルタイムバーが閉じると経過したリアルタイムバーとなり、新しいリアルタイムバーが開く)
  • スクリプトがチャートにロードされたとき
  • リアルタイムバーが更新が発生したとき(およびリアルタイムバーが開くとき)
  • リアルタイムバーが閉じるとき
  • スクリプトがチャートにロードされたとき(※)、ヒストリカルバー上でスクリプトが実行される。
    (※)
    ・チャートのシンボルまたは時間足を変更したとき
    ・スクリプトを保存またはチャートへ追加したとき
    ・インジケーターまたはストラテジーのパラメーターを変更したとき
    ・ブラウザーの refresh イベントを検知したとき
  • データセットの左から右へ (最初から最後まで) 各バーにつき 1 回ずつ繰り返し実行される。
  • 組み込み変数 open, high, low, close, volume, time の値には終値時点のものが設定される。
  • ヒストリカルバー上の実行が最右端に達し、市場がオープンしているとき、リアルタイムバー上の実行に移る。
  • インジケーターの場合、リアルタイムバーの更新が発生するたび(およびリアルタイムバーが開くとき)にそのリアルタイムバー上でスクリプトが 1 回実行される。
    • 実行ごとに high, low, close など組み込み変数の値が変化し、それに従ってインジケーターの計算結果も変わる可能性。
    • リアルタイムバー上では close は、現在の価格を表す。同様に、highlow は、リアルタイムバーの開始以降に到達した最高値と最安値を表す。
    • リアルタイムバー上の実行前、スクリプトのユーザー定義変数と描画は前回開始前にリセット(ロールバック)される。
  • ストラテジーの場合、リアルタイムバーの更新が発生したとき(およびリアルタイムバーが開くとき)、デフォルトでは実行されない。ただし、インジケーターのように更新されるたびに実行するように設定することもできる。
    更新されるたびに実行するかどうかは strategy 宣言部の calc_on_every_tick パラメーターで設定する。詳細は ストラテジー.計算動作の変更 を参照。
  • リアルタイムバーが閉じるときにもリアルタイムバー上でスクリプトが 1 回実行される。
    • このときの open, high, low, close, volume, time 組み込み変数の値は、最終的な値としてコミットされる。

barstate.* 組み込み変数を使うと、バーの種類やスクリプトが実行されているイベントの情報を取得することができる。
例えば、下のスクリプトはヒストリカルバーと経過したリアルタイムバーの違いを視覚化している。
ヒストリカルバーの場合には 1 がプロットされ、リアルタイムバー上ではバーの更新回数をカウントしてプロットする。

//@version=5
indicator("")
updateNo() =>
    varip int updateNo = na
    if barstate.isnew
        updateNo := 1
    else
        updateNo += 1
plot(updateNo())

Pine スクリプトにおいて、すべての関数呼び出しは、後続のバーから [] を使ってアクセスできる履歴を残す。
ただし、関数がすべてのバーで呼び出されない場合、その計算結果は一貫性がないものになる可能性があるため留意が必要である。

下のスクリプトを見てみる。 異なる方法でバーのインデックスを 3種類 プロットしている。

  1. 組み込み変数 bar_index を緑色でプロット
  2. calcBarIndex1() 関数をすべてのバーで呼び出して取得した結果 customIndex1 を赤色でプロット
  3. calcBarIndex2() 関数を 1 本おきに呼び出して取得した結果 customIndex2 を黄色でプロット
//@version=5
indicator("My script")
 
//@function 前のバーのバーインデックスに 1 を足すことで現在のバーのバーインデックスを取得する
// 最初のバーのインデックスは 0 
calcBarIndex1() =>
    int index = na
    index := nz(index[1], replacement = -1) + 1
 
calcBarIndex2() =>
    int index = na
    index := nz(index[1], replacement = -1) + 1
 
//@variable 1 本おきに true を返す
condition = bar_index % 2 == 0
 
globalScopeBarIndex = calcBarIndex1()
int customIndex1 = na
int customIndex2 = na
 
// condition が true のとき、customIndex1 に globalScopeBarIndex を、customIndex2 に calcBarIndex2() を代入
if condition
    customIndex1 := globalScopeBarIndex // コンパイラに警告されない
    customIndex2 := calcBarIndex2()     // コンパイラに警告される 意図通りに動作するかは関数履歴に依る
 
plot(bar_index,   "Bar index",    color = color.green)
plot(customIndex1, "Custom index1", color = color.red, style = plot.style_cross)
plot(customIndex2, "Custom index2", color = color.yellow, style = plot.style_cross)

これをチャートに貼り付けると以下のようになる。
緑色 (bar_index) と 赤色 (customIndex1) は同じであるのに対し、黄色 (customIndex2) は異なる結果になる。

黄色 (customIndex2) だけ異なるのは、if condition のスコープでバー 1 本おきに calcBarIndex2() 関数を呼び出していることが原因である。
calcBarIndex2() 関数内部では index 変数の 1 本前の履歴を参照しようとするが、関数は 1 本おきにしか実行されないので 1 本前の履歴は存在しない。このとき 2 本前の履歴が参照される。

このようなコードが書かれた場合、コンパイラは計算の一貫性について以下のような警告をだす。

コメントを入力:
 

  • tradingview/pine/execution_model.txt
  • 最終更新: 2023/08/25 12:58
  • by 管理人