Pineスクリプト作成代行はこちら

Pineスクリプトの書き方|基本構文をコード付きで完全解説

Pineスクリプトを書いてみたいけど、何から始めればいいかわからない。公式リファレンスを見ても情報が多すぎて、最初の一歩が踏み出せない。

Pineスクリプトの構文はシンプルだ。PythonやJavaScriptを知らなくても、基本的な書き方のパターンさえ押さえれば、インジケーターもストラテジーも書ける。実際、Pineスクリプトはトレーダーが使うことを前提に設計された言語で、汎用プログラミング言語よりもはるかに覚えることが少ない。

この記事では、Pineスクリプトの基本構文をv6ベースで体系的に解説する。すべてのセクションにコピペで動くコード例を付けたので、Pineエディタに貼り付けて実際に動かしながら読み進めてほしい。

Pineスクリプトの全体像と活用法を見る

スクリプトの基本構造

Pineスクリプトは、どんなコードでも必ず同じ3つのパートで構成される。この構造を頭に入れておけば、他人のコードを読むときにも迷わない。

//@version=6                           // ① バージョン宣言
indicator("マイインジケーター", overlay=true) // ② スクリプト種別宣言
plot(close)                            // ③ ロジック+出力

① バージョン宣言は必ず1行目に書く。2025年現在の最新はv6だ。//@version=6と書けばいい。古いサンプルコードでは//@version=5//@version=4になっているが、新規で書く場合はv6を使うこと。

② スクリプト種別宣言は、このスクリプトが何をするものかをTradingViewに伝える部分だ。選択肢は2つ。チャートに線やシグナルを表示するならindicator()。売買のバックテストをするならstrategy()。どちらか一方を必ず記述する。

③ ロジック+出力がスクリプトの本体だ。計算処理を書き、plot()などの出力関数でチャートに結果を表示する。

indicator()とstrategy()の違い

スクリプト種別宣言をもう少し掘り下げる。indicator()strategy()はそれぞれ異なる引数を持っており、スクリプト全体の挙動に影響する。

indicator()の基本的な書き方:

//@version=6
indicator("RSIインジケーター", overlay=false)
rsiValue = ta.rsi(close, 14)
plot(rsiValue, "RSI", color.purple)
hline(70, "買われすぎ", color.red)
hline(30, "売られすぎ", color.green)

overlay=trueにするとメインチャートに重ねて表示される。移動平均線やボリンジャーバンドなど、ローソク足と同じスケールのインジケーターで使う。overlay=falseにすると、チャート下部に別パネルとして表示される。RSIやMACDなど、価格とは異なるスケールのオシレーター系で使う。

strategy()の基本的な書き方:

//@version=6
strategy("SMAクロス", overlay=true, default_qty_type=strategy.percent_of_equity, default_qty_value=100)
smaFast = ta.sma(close, 20)
smaSlow = ta.sma(close, 50)
if ta.crossover(smaFast, smaSlow)
    strategy.entry("Buy", strategy.long)
if ta.crossunder(smaFast, smaSlow)
    strategy.close("Buy")
plot(smaFast, "SMA20", color.blue)
plot(smaSlow, "SMA50", color.red)

strategy()には注文数量の設定(default_qty_typedefault_qty_value)や初期資金(initial_capital)など、バックテスト固有のパラメータがある。

Pineスクリプトがそもそも何かを知りたい方は、こちらの記事で全体像を掴んでから戻ってくるとスムーズだ。

Pineスクリプト(Pine Script)とは?できること・始め方を完全解説

変数の宣言と型

Pineスクリプトの変数宣言は直感的だ。型を明示することもできるし、省略して自動推論に任せることもできる。

//@version=6
indicator("変数の例", overlay=true)

// 型を省略(自動推論)
length = 20
src = close

// 型を明示
int length2 = 50
float threshold = 1.5
bool isUpTrend = close > ta.sma(close, 200)
string label_text = "トレンド"
color lineColor = isUpTrend ? color.green : color.red

smaValue = ta.sma(src, length)
plot(smaValue, "SMA", lineColor, 2)

基本型は5つだ。int(整数)、float(小数)、bool(真偽値)、string(文字列)、color(色)。これに加えて、linelabelboxtableといった描画オブジェクト型がある。

型を明示する必要があるのは、変数の初期値がna(値なし)のときだ。Pineスクリプトでは初期値がnaだと型を推論できないため、明示が必要になる。

float previousHigh = na   // OK: 型を明示
// previousHigh = na      // NG: 型がわからない

varとvaripの使い分け

Pineスクリプト特有の概念が「バーごとの実行」だ。スクリプトはチャートの左端から右端に向かって、1本のバーごとに繰り返し実行される。変数の宣言方法によって、値の保持のされ方が変わる。

//@version=6
indicator("var と varip の違い")

// 通常の変数: バーごとに再初期化される
count1 = 0
count1 := count1 + 1   // 常に 1

// var変数: 最初のバーでのみ初期化。以降は値を保持
var int count2 = 0
count2 := count2 + 1   // 1, 2, 3, 4, ...

// varip変数: リアルタイムバーのティックごとに更新
varip int count3 = 0
count3 := count3 + 1   // ヒストリカルは var と同じ。リアルタイムはティックごとに加算

plot(count1, "通常", color.red)
plot(count2, "var", color.blue)
plot(count3, "varip", color.green)

varなしで宣言した変数は、バーごとにリセットされる。前のバーの値を保持したい場合はvarを付ける。これはカウンター、フラグ、累計値の管理で頻繁に使うパターンだ。

varipvarの拡張版で、リアルタイムバー(確定前の最新バー)でティックごとに値が更新される。ヒストリカルバーではvarと同じ挙動になる。リアルタイムの価格変動を追跡したい場合に使う。

演算子

Pineスクリプトの演算子は他のプログラミング言語とほぼ同じだ。

算術演算子は+(加算)、-(減算)、*(乗算)、/(除算)、%(剰余)。

比較演算子は==(等しい)、!=(等しくない)、>(より大きい)、<(より小さい)、>=(以上)、<=(以下)。

論理演算子はandornot。v6ではこれらが遅延評価されるため、andの左側がfalseなら右側は評価されない。

代入演算子は=(初回代入)と:=(再代入)。Pineスクリプトでは初回の代入と再代入で演算子を使い分ける必要がある。

//@version=6
indicator("演算子の例")

// 初回代入は =
a = 10
b = 3

// 再代入は :=
var int total = 0
total := total + 1

// 比較と論理
isAbove = close > ta.sma(close, 20) and volume > ta.sma(volume, 20)

plot(isAbove ? 1 : 0, "条件成立")

特に重要なのは=:=の違いだ。最初に変数に値を入れるときは=。すでに宣言済みの変数に新しい値を入れるときは:=。これを間違えるとコンパイルエラーになる。

条件分岐(if文とswitch文)

条件分岐の書き方は2種類ある。

if文はシンプルな条件分岐に使う:

//@version=6
indicator("if文の例", overlay=true)
sma20 = ta.sma(close, 20)

// 基本形
if close > sma20
    label.new(bar_index, high, "上")

// if-else
direction = if close > sma20
    "上昇"
else
    "下降"

// if-else if-else
var color bgColor = na
if close > sma20 * 1.02
    bgColor := color.new(color.green, 80)
else if close < sma20 * 0.98
    bgColor := color.new(color.red, 80)
else
    bgColor := na

bgcolor(bgColor)
plot(sma20, "SMA20", color.orange)

Pineスクリプトのインデントにはスペース4つを使う。タブでもスペース2つでもなく、スペース4つだ。インデントが正しくないとコンパイルエラーになるので注意すること。

switch文は複数の条件を整理するときに便利だ:

//@version=6
indicator("switch文の例")
rsi = ta.rsi(close, 14)

zone = switch
    rsi >= 70 => "買われすぎ"
    rsi <= 30 => "売られすぎ"
    => "中立"

plotchar(zone == "買われすぎ", "Over", "▲", location.top, color.red)
plotchar(zone == "売られすぎ", "Under", "▼", location.bottom, color.green)

v6ではswitch文に必ずデフォルトケース(=>で始まる行)を含める必要がある。省略するとコンパイルエラーになる。

ループ(forとwhile)

繰り返し処理にはforループとwhileループがある。

//@version=6
indicator("forループの例")

// 過去10本のバーの最高値を取得
highestInRange = high
for i = 1 to 9
    if high[i] > highestInRange
        highestInRange := high[i]

plot(highestInRange, "過去10本の最高値", color.blue)

// for...in でコレクションを処理
periods = array.from(10, 20, 50)
var label infoLabel = na
label.delete(infoLabel)
labelText = ""
for period in periods
    smaVal = ta.sma(close, period)
    labelText := labelText + "SMA" + str.tostring(period) + ": " + str.tostring(smaVal, "#.##") + "\n"
infoLabel := label.new(bar_index, high, labelText, style=label.style_label_down)

whileループは条件がfalseになるまで実行を続ける:

//@version=6
indicator("whileループの例")

// ボリュームが平均以上のバーを過去から探す
avgVol = ta.sma(volume, 20)
var int barsBack = 0
i = 1
while i <= 100
    if volume[i] > avgVol * 2
        barsBack := i
        break
    i := i + 1

plot(barsBack, "大出来高のバー数前")

ループ内でbreakを使うと途中で抜けられる。continueを使うと現在の反復をスキップして次に進む。

関数の定義

Pineスクリプトでは、自分でオリジナルの関数を定義できる。繰り返し使う処理をまとめるのに便利だ。

//@version=6
indicator("関数定義の例", overlay=true)

// 単一行の関数(アロー記法)
percent(a, b) => (a - b) / b * 100

// 複数行の関数
getSignal(fastLen, slowLen) =>
    fast = ta.ema(close, fastLen)
    slow = ta.ema(close, slowLen)
    signal = ta.crossover(fast, slow) ? 1 : ta.crossunder(fast, slow) ? -1 : 0
    [fast, slow, signal]   // 複数の値を返す

// 関数の呼び出し
[emaFast, emaSlow, sig] = getSignal(12, 26)

plot(emaFast, "EMA12", color.blue)
plot(emaSlow, "EMA26", color.red)
plotshape(sig == 1, "買い", shape.triangleup, location.belowbar, color.green)
plotshape(sig == -1, "売り", shape.triangledown, location.abovebar, color.red)

関数は[値1, 値2]のように複数の値を返すことができる。受け取り側も[変数1, 変数2] = 関数名(引数)の形式で書く。

動くコードをすぐに試したい方はこちら

input関数でユーザー入力を受け付ける

input関数を使うと、インジケーターの設定画面からパラメータを変更できるようになる。コードを書き換えずに数値を調整できるので、実用的なスクリプトには必須だ。

//@version=6
indicator("input関数の例", overlay=true)

// 数値入力
length = input.int(20, "期間", minval=1, maxval=200, step=1, group="SMA設定")
src = input.source(close, "ソース", group="SMA設定")

// 色入力
lineColor = input.color(color.blue, "線の色", group="表示設定")
lineWidth = input.int(2, "線の太さ", minval=1, maxval=5, group="表示設定")

// ON/OFF切り替え
showBand = input.bool(true, "バンド表示", group="表示設定")
bandMult = input.float(2.0, "バンド幅(倍率)", minval=0.5, maxval=5.0, step=0.1, group="SMA設定")

// 計算と表示
smaValue = ta.sma(src, length)
dev = ta.stdev(src, length) * bandMult

plot(smaValue, "SMA", lineColor, lineWidth)
plot(showBand ? smaValue + dev : na, "Upper", color.new(lineColor, 60))
plot(showBand ? smaValue - dev : na, "Lower", color.new(lineColor, 60))

input関数は型ごとに専用の関数が用意されている。input.int(整数)、input.float(小数)、input.bool(真偽値)、input.string(文字列)、input.color(色)、input.source(データソース)など。

group引数を使うと、設定画面でパラメータをグループ分けできる。項目が多いインジケーターでは整理のために必須だ。tooltip引数でマウスオーバー時のヒント文を追加することもできる。

ヒストリー参照演算子([])

Pineスクリプト特有の重要な構文が、ヒストリー参照演算子[]だ。過去のバーの値を取得するために使う。

//@version=6
indicator("ヒストリー参照の例")

// 1本前の終値
prevClose = close[1]

// 2本前の高値
prevPrevHigh = high[2]

// 変化率の計算
change1 = close - close[1]           // 前日比
changePct = (close - close[1]) / close[1] * 100   // 前日比(%)

plot(changePct, "前日比%", color.blue)
hline(0, "ゼロライン")

close[1]で1本前のバーの終値、close[2]で2本前、という具合に数字を増やしていく。close[0]は現在のバーの終値で、closeと同じだ。

この演算子はseries型の値にのみ使える。v6ではリテラル(数値や文字列の固定値)にヒストリー参照を付けることはできなくなったので注意すること。たとえば100[1]はv6ではエラーになる。

組み込み変数

Pineスクリプトには、よく使うデータがあらかじめ変数として用意されている。

価格データはopen(始値)、high(高値)、low(安値)、close(終値)、volume(出来高)、hl2(高値と安値の中間値)、hlc3(高値・安値・終値の平均)、ohlc4(4本値の平均)。

時間データはtime(バーの時刻)、bar_index(バーのインデックス番号)、dayofweek(曜日)、hour(時)、minute(分)。

バー情報はbarstate.isconfirmed(バーが確定したか)、barstate.islast(最新バーか)、barstate.isrealtime(リアルタイムバーか)。

シンボル情報はsyminfo.ticker(ティッカー名)、syminfo.currency(通貨)、syminfo.mintick(最小ティック)。

これらはすべて宣言不要でそのまま使える。

//@version=6
indicator("組み込み変数の例", overlay=true)
// 金曜日の終値に色をつける
isFriday = dayofweek == dayofweek.friday
barColor = isFriday ? color.orange : na
barcolor(barColor)

テクニカル分析関数(ta.系)

ta.名前空間には50以上のテクニカル分析関数がある。よく使うものを紹介する。

//@version=6
indicator("テクニカル関数の例", overlay=false)

// 移動平均
sma20 = ta.sma(close, 20)      // 単純移動平均
ema20 = ta.ema(close, 20)      // 指数移動平均

// オシレーター
rsi = ta.rsi(close, 14)         // RSI
[macdLine, signalLine, histogram] = ta.macd(close, 12, 26, 9)  // MACD

// ボラティリティ
atr = ta.atr(14)                // ATR
[upper, middle, lower] = ta.bb(close, 20, 2.0)   // ボリンジャーバンド

// クロス判定
goldenCross = ta.crossover(sma20, ta.sma(close, 50))   // ゴールデンクロス
deadCross = ta.crossunder(sma20, ta.sma(close, 50))     // デッドクロス

// 最高値・最安値
highest20 = ta.highest(high, 20)   // 過去20本の最高値
lowest20 = ta.lowest(low, 20)      // 過去20本の最安値

plot(rsi, "RSI", color.purple)
hline(70)
hline(30)

ta.macdta.bbのように複数の値を返す関数は、[変数1, 変数2, 変数3] = 関数()の形式で受け取る。

出力関数(plot系)

計算結果をチャートに表示するための出力関数を整理する。

//@version=6
indicator("出力関数の例", overlay=true)
sma = ta.sma(close, 20)
isUp = close > sma

// 基本的なライン描画
plot(sma, "SMA", color.blue, 2)

// 条件付きの色分け
plot(close, "終値", isUp ? color.green : color.red, style=plot.style_linebr)

// バーの色を変更
barcolor(isUp ? color.green : color.red)

// 背景色を変更
bgcolor(isUp ? color.new(color.green, 90) : color.new(color.red, 90))

// シェイプ(矢印やマーク)の表示
plotshape(ta.crossover(close, sma), "上抜け", shape.triangleup, location.belowbar, color.green, size=size.small)
plotshape(ta.crossunder(close, sma), "下抜け", shape.triangledown, location.abovebar, color.red, size=size.small)

// 水平線
hline(0, "ゼロライン", color.gray, hline.style_dashed)

plot()はライン描画の基本。plotshape()は条件成立時に矢印やマークを表示する。barcolor()はローソク足の色を変える。bgcolor()は背景色を変える。hline()は水平線を引く。

色の透明度はcolor.new(色, 透明度)で指定する。透明度は0(不透明)から100(完全透明)の範囲だ。

アラートの設定

price action やテクニカル条件にもとづいてアラートを鳴らすコードの書き方だ。

//@version=6
indicator("アラートの例", overlay=true)
sma = ta.sma(close, 20)

// alertcondition: アラート作成ダイアログで使用可能な条件を定義
alertcondition(ta.crossover(close, sma), "SMA上抜け", "{{ticker}}: 終値がSMA20を上抜けました")
alertcondition(ta.crossunder(close, sma), "SMA下抜け", "{{ticker}}: 終値がSMA20を下抜けました")

// alert(): スクリプト実行中に直接アラートを発火
if ta.crossover(close, sma)
    alert("SMA上抜け: " + syminfo.ticker + " 価格=" + str.tostring(close), alert.freq_once_per_bar)

plot(sma, "SMA20", color.blue)

alertcondition()は条件を登録するだけで、実際にアラートを有効にするにはTradingView上で手動設定が必要だ。alert()は直接アラートを発火する関数で、Webhookとの連携にも使える。

メッセージ内の{{ticker}}{{close}}はTradingViewが自動で実際の値に置換してくれるプレースホルダだ。

ストラテジーの書き方

バックテスト用のストラテジーを書く基本パターンを示す。

//@version=6
strategy("EMAクロス戦略", overlay=true, initial_capital=100000, default_qty_type=strategy.percent_of_equity, default_qty_value=100, commission_type=strategy.commission.percent, commission_value=0.1)

// パラメータ
fastLen = input.int(12, "短期EMA")
slowLen = input.int(26, "長期EMA")
slPct = input.float(2.0, "損切り%")
tpPct = input.float(4.0, "利確%")

// 計算
emaFast = ta.ema(close, fastLen)
emaSlow = ta.ema(close, slowLen)

// エントリー条件
longCondition = ta.crossover(emaFast, emaSlow)
shortCondition = ta.crossunder(emaFast, emaSlow)

// 注文
if longCondition
    strategy.entry("Long", strategy.long)
    strategy.exit("Long Exit", from_entry="Long", profit=close * tpPct / 100 / syminfo.mintick, loss=close * slPct / 100 / syminfo.mintick)

if shortCondition
    strategy.close("Long")

// 表示
plot(emaFast, "EMA Fast", color.blue)
plot(emaSlow, "EMA Slow", color.red)
bgcolor(strategy.position_size > 0 ? color.new(color.green, 90) : na)

strategy.entry()でエントリー、strategy.exit()で利確・損切り、strategy.close()で全ポジション決済。v6ではstrategy.exit()from_entryパラメータが必須なので、必ずエントリーIDを指定すること。

strategy.position_sizeは現在のポジションサイズを返す変数で、ポジションの有無を判定するのに使える。

コピペで使える実用的なサンプルをもっと見たい方はこちら。

Pineスクリプトサンプルコード集|コピペで動く実用インジケーター10選

Pineスクリプトの実践的な活用法を探す

描画オブジェクト(line, label, box, table)

チャート上に動的な図形やテキストを描画するオブジェクト系の書き方だ。

//@version=6
indicator("描画オブジェクトの例", overlay=true)

// ラベル: 最新バーに情報を表示
if barstate.islast
    label.new(bar_index, high, "現在値: " + str.tostring(close, "#.##"), style=label.style_label_down, color=color.blue, textcolor=color.white)

// ライン: 直近の最高値に水平線
var line highLine = na
line.delete(highLine)
h20 = ta.highest(high, 20)
highLine := line.new(bar_index - 20, h20, bar_index, h20, color=color.red, style=line.style_dashed, width=2)

// テーブル: 画面端に情報パネル
var table infoTable = table.new(position.top_right, 2, 3, bgcolor=color.new(color.black, 80), border_width=1)
if barstate.islast
    table.cell(infoTable, 0, 0, "SMA20", text_color=color.gray, text_size=size.small)
    table.cell(infoTable, 1, 0, str.tostring(ta.sma(close, 20), "#.##"), text_color=color.white, text_size=size.small)
    table.cell(infoTable, 0, 1, "RSI", text_color=color.gray, text_size=size.small)
    table.cell(infoTable, 1, 1, str.tostring(ta.rsi(close, 14), "#.##"), text_color=color.white, text_size=size.small)
    table.cell(infoTable, 0, 2, "ATR", text_color=color.gray, text_size=size.small)
    table.cell(infoTable, 1, 2, str.tostring(ta.atr(14), "#.##"), text_color=color.white, text_size=size.small)

描画オブジェクトはvarで宣言してバーごとに削除→再作成するパターンが基本だ。varを使わないとバーごとにオブジェクトが増え続けてしまう。barstate.islastで最新バーでのみ実行する条件を加えると、不要な描画を防げる。

tableはチャートの端に固定されるパネルで、リアルタイムの情報表示に便利だ。position.top_rightなどで配置位置を指定する。

request.security()で別の時間足を取得

マルチタイムフレーム分析をするには、request.security()で別の時間足のデータを取得する。

//@version=6
indicator("マルチタイムフレームの例", overlay=true)

// 日足のSMA20を15分足チャートに表示
dailySMA = request.security(syminfo.tickerid, "D", ta.sma(close, 20))
plot(dailySMA, "日足SMA20", color.orange, 2)

// 4時間足のRSIを取得
h4RSI = request.security(syminfo.tickerid, "240", ta.rsi(close, 14))
plotchar(h4RSI > 70, "4H RSI > 70", "●", location.abovebar, color.red, size=size.tiny)

第2引数の時間足は"D"(日足)、"W"(週足)、"M"(月足)、"240"(4時間足=240分)のように文字列で指定する。v6ではこの引数にseries stringを渡せるようになり、動的に時間足を変更できるようになった。

コメントの書き方

コードにコメントを付ける方法は2種類ある。

// 1行コメント: スラッシュ2つ
sma = ta.sma(close, 20)   // 行末コメントも可能

/* 
   複数行コメント:
   スラッシュ+アスタリスクで囲む
   長い説明を書くときに便利
*/

コメントはコード量が増えてきたときに必ず役立つ。特にinputのパラメータが多いスクリプトや、複雑なロジックを含むストラテジーでは、各セクションにコメントを入れる習慣をつけておくとメンテナンスが楽になる。

よくあるエラーと対処法

Pineスクリプトを書いていて遭遇しやすいエラーを5つ紹介する。

「Mismatched input」 — インデントのミス。Pineスクリプトはスペース4つのインデントが必須。タブ文字やスペース2つでは動かない。

「Cannot call '関数名' with argument」 — 関数に渡した引数の型が間違っている。リファレンスで正しい型を確認する。

「Undeclared identifier」 — 変数名のタイプミス。または、変数を宣言する前に使おうとしている。

「Script has too many local scopes」 — v5でスコープ数の制限(550)を超えた。v6ではこの制限が撤廃されている。

「The 'when' argument is not supported」 — v6で廃止されたwhenパラメータを使っている。if文に書き換える。

エラーメッセージは必ず英語で表示される。メッセージ内のキーワードでリファレンスを検索すれば、正しい書き方が見つかる。

まとめ

Pineスクリプトの書き方は「バージョン宣言 → 種別宣言 → ロジック+出力」の3パートが基本だ。変数宣言、条件分岐、ループ、関数定義、input設定、出力関数、テクニカル分析関数。この記事で解説した構文を組み合わせれば、インジケーターもストラテジーもゼロから書ける。

最初は短いコードから始めて、動くことを確認しながら少しずつ要素を追加していくのがおすすめだ。一度に完璧なコードを書こうとする必要はない。

Pineスクリプトの応用テクニックと実践コードはこちら

※当サイトの内容は投資助言を目的としたものではありません。FX取引にはリスクが伴い、投資元本を失う可能性があります。投資判断はご自身の責任でお願いいたします。