TradingView Pine Script入門:FXインジケーターの作り方【v5完全ガイド】
Photo by Maxim Hopman on Unsplash
最終更新: 2026年07月
TradingViewは2025年時点で月間アクティブユーザー4億人超を持つチャートプラットフォームだ(出典: TradingView公式)。そこで動くスクリプト言語が Pine Script。HTMLやJavaScriptを知らなくても、数十行のコードでEMAクロスやRSIフィルターを実装できる。ただしその手軽さの裏には「バックテスト精度の構造的限界」が潜む。文法から実装、陥りやすい落とし穴まで、金融工学の視点で一気通貫に整理しておく。
Pine Scriptとは何か — バージョン履歴と設計思想
Pine Scriptは、TradingViewが独自に開発した ドメイン固有言語(DSL: Domain-Specific Language) だ。汎用プログラミング言語ではなく、チャート分析と可視化に特化して設計されている。
設計上の最大の特徴は 「バーごとの逐次実行モデル」 にある。コードは過去の最古バーから現在バーに向かって1本ずつ実行される。これはPythonのループ処理と見た目は似るが、内部的にはスクリプトエンジンが自動的にイテレーションを管理する。この仕組みのおかげで ta.ema(close, 9) と書くだけでEMAの計算履歴が自動的に保持される。
バージョン変遷は下記のとおり。
| バージョン | リリース | 主な特徴 |
|---|---|---|
| v3 | 2020年以前 | レガシー。現在はほぼ使用されない |
| v4 | 2020〜 | study() / security() / sma() 等の直接関数名 |
| v5 | 2021年〜 | 名前空間導入(ta.sma()等)。現行の主流 |
| v6 | 2024年12月10日 | 最新版。型推論強化・request関数のseries string対応(Dynamic Requests)追加(出典: TradingView公式ブログ) |
現時点でコミュニティスクリプトの大多数はv5で書かれている。TradingView上で公開されているスクリプト数は150,000本超(出典: TradingView公式)。新規に書くならv5を土台にして、v6の新機能は必要に応じて取り込めばいい。
Pine Scriptエディタの開き方と基本UI操作
エディタを開くまでの手順は単純だ。
- TradingViewにログイン後、任意のチャート画面を開く
- 画面下部の「Pine Scriptエディタ」タブをクリック(ショートカット: Alt + P)
- 新規作成の場合は「+」アイコンをクリックして「空のインジケーター」を選択
- コードを入力後、右上の「追加(チャートへ)」ボタンを押すと即座にチャートへ反映される
エディタはリアルタイムのシンタックスハイライトと自動補完を備える。ta. と入力するだけでEMA・RSI・MACD等の関数候補がドロップダウン表示される。
無料プランの制限: TradingViewの無料プランでは、チャートに追加できるインジケーターは最大2本。自作スクリプトもこの上限に含まれる。複合インジケーターを1本に統合すると、実質的にこの制限を回避できる。
Pine Scriptの基本文法
Pine Scriptの型システムは静的型付けだが、型推論により宣言を省略できる。series float型——バーごとに異なる値を持つ数値列——の理解が、言語全体を貫く核心概念だ。
変数・型・演算子
型宣言の省略はほとんどの場面でコンパイラに任せられる。
//@version=5
indicator("文法サンプル", overlay=false)
// 整数型(明示的宣言)
var int counter = 0
// 浮動小数点型(型推論)
myValue = 3.14
// 文字列型
myLabel = "ゴールデンクロス"
// 真偽値型(bool)
isAbove = close > open // ローソク足が陽線かどうか
// シリーズ型: バーごとに異なる値を持つ数値列
// close, open, high, low は組み込みのseries float
closeSeries = close // これは series float 型
var キーワードは変数を最初のバーのみ初期化し、以降はバーをまたいで値を保持する。これが「カウンター」や「累積値」の実装に必要な概念だ。var なしの場合、各バーで毎回初期化される。
条件分岐
// if-else 構文
trend = if close > ta.ema(close, 50)
"上昇トレンド"
else
"下降トレンド"
// 三項演算子(v5以降推奨)
signal = close > open ? 1 : -1
// switch 構文(v5以降)
trendStrength = switch
close > ta.ema(close, 200) => "強い上昇"
close > ta.ema(close, 50) => "弱い上昇"
=> "下降"
v4で使われていた iff(条件, a, b) は v5で廃止。条件 ? a : b の三項演算子に移行が必要だが、この変換は 自動化ツールでは対応されない。手動での書き換えが必要だ。
ループと関数定義
// for ループ: 配列の各要素を処理する場面で使用
sumValues = 0.0
for i = 0 to 9
sumValues := sumValues + close[i] // [i] で i本前の値を参照
// カスタム関数定義
// 引数と戻り値の型はコンパイラが推論する
myEma(src, length) =>
alpha = 2 / (length + 1)
var float result = na
result := na(result) ? src : alpha * src + (1 - alpha) * result
result
close[1] で1本前のclose、close[10] で10本前のcloseを取得できる。この [] 演算子がPine Scriptのバーアクセスの核心で、Pythonのリストインデックスとは逆向き(過去方向が正の整数)という点は、最初に確認しておきたい。
実践1 — EMAクロスインジケーターの実装
Photo by Anne Nygård on Unsplash
EMAクロスは「短期EMAが長期EMAを上抜けたら買い、下抜けたら売り」という古典的なモメンタム戦略だ。数理的には2本の指数移動平均のクロスを検出するために ta.crossover() と ta.crossunder() を使う。
//@version=5
indicator("EMA Cross Signal", overlay=true)
// 入力パラメーター
fastLen = input.int(9, title="Fast EMA 期間", minval=1)
slowLen = input.int(21, title="Slow EMA 期間", minval=1)
// EMA計算
// ta.ema(source, length) は組み込みの指数移動平均関数
fastEMA = ta.ema(close, fastLen)
slowEMA = ta.ema(close, slowLen)
// プロット
plot(fastEMA, title="Fast EMA", color=color.green, linewidth=2)
plot(slowEMA, title="Slow EMA", color=color.red, linewidth=2)
// クロス判定
// ta.crossover(a, b): a が b を下から上に突き抜けた瞬間に true
longSig = ta.crossover(fastEMA, slowEMA)
// ta.crossunder(a, b): a が b を上から下に突き抜けた瞬間に true
shortSig = ta.crossunder(fastEMA, slowEMA)
// シグナル表示: チャート上に矢印を描画
plotshape(longSig, title="Buy", style=shape.triangleup,
location=location.belowbar, color=color.green, size=size.small)
plotshape(shortSig, title="Sell", style=shape.triangledown,
location=location.abovebar, color=color.red, size=size.small)
// アラート条件の登録
alertcondition(longSig, title="EMA ゴールデンクロス", message="EMA ゴールデンクロス発生")
alertcondition(shortSig, title="EMA デッドクロス", message="EMA デッドクロス発生")
input.int() で設定パネルからパラメーターを変更可能にしている。minval=1 はユーザーが0以下を入力しても実行エラーになるのを防ぐバリデーションだ。
同じEMAクロスロジックをMQL4/MQL5のEAとして実装したい場合は、EMAクロスEAをMQL4で実装するコード全解説が参考になる。
実践2 — RSI + 移動平均フィルター複合インジケーター
Photo by Mohammad Rahmani on Unsplash
単純なRSIだけでは「下降トレンド中の売られすぎ」に対して誤ったロングシグナルを出す問題がある。移動平均によるトレンドフィルターを加えると、シグナルの精度を構造的に底上げできる。
直感的には「RSIが30以下なら買い」で十分に見えるが、数理的には「EMAを上回っている相場でのみRSI30割れを買いシグナルとみなす」という条件付き確率のロジックになる。
RSIの計算式や設定値の数理的背景については、RSI(相対力指数)のFX活用法と計算式解説も参照してほしい。
//@version=5
indicator("RSI + EMA Filter", overlay=false)
// 入力
rsiLen = input.int(14, title="RSI 期間", minval=1)
maLen = input.int(50, title="EMA フィルター期間", minval=1)
rsiOB = input.int(70, title="買われすぎライン")
rsiOS = input.int(30, title="売られすぎライン")
// 計算
rsiVal = ta.rsi(close, rsiLen) // RSI計算
emaVal = ta.ema(close, maLen) // EMAフィルター計算
// RSIのプロット(サブウィンドウに表示: overlay=false)
plot(rsiVal, title="RSI", color=color.blue, linewidth=2)
hline(rsiOB, "買われすぎ", color=color.red)
hline(rsiOS, "売られすぎ", color=color.green)
hline(50, "中間ライン", color=color.gray, linestyle=hline.style_dotted)
// フィルター判定
isUpTrend = close > emaVal // 現在足がEMAより上 = 上昇トレンド
isDownTrend = close < emaVal // 現在足がEMAより下 = 下降トレンド
// 背景色でゾーンを可視化
// color.new(色, 透明度) の透明度は0(不透明)〜100(完全透明)
bgcolor(isUpTrend and rsiVal < rsiOS ? color.new(color.green, 85) : na,
title="Buy Zone")
bgcolor(isDownTrend and rsiVal > rsiOB ? color.new(color.red, 85) : na,
title="Sell Zone")
// EMAフィルター付きアラート
alertcondition(isUpTrend and ta.crossover(rsiVal, rsiOS),
title="RSI 買いシグナル(EMAフィルター済み)",
message="上昇トレンド中にRSIが売られすぎから回復")
このインジケーターは単独では売買判断に使えない。背景色が点灯したとしても、それはあくまで「条件が揃ったゾーン」を示すものに過ぎず、次の足で条件が崩れる場合もある。そこのリスクは、後述するバックテストの限界と合わせて頭に入れておいてほしい。
plot()・bgcolor()・alertcondition()の詳細
plot()・bgcolor()・alertcondition()はPine Scriptの描画・背景色・アラート設定を担う3つの核心関数だ。主要オプションを確認しておく。
plot() の主要オプション
plot(
series, // 描画するシリーズ(必須)
title="タイトル", // 設定パネルに表示されるラベル
color=color.blue, // 線の色
linewidth=2, // 線幅(1〜4)
style=plot.style_line // スタイル: line/columns/histogram/area 等
)
overlay=true を indicator() に指定するとメインチャート上に重ねて描画し、overlay=false でサブウィンドウに表示される。RSIのようにスケールが異なる指標は必ず overlay=false を使う。
bgcolor() の使い方
// na を渡すと描画しない(条件付き背景色に便利)
bgcolor(condition ? color.new(color.green, 80) : na)
透明度を80〜90に設定するとローソク足が透けて見えるため視認性を保てる。
alertcondition() の注意点
alertcondition() はアラートの「テンプレート」を登録するだけだ。実際にアラートを発火させるには、チャート上でベルアイコン → 「アラートを作成」→ 作成したコンディションを選択する操作が別途必要になる。コードを書いただけではアラートは届かない。
コミュニティスクリプトへの公開方法
自作スクリプトをTradingViewのコミュニティに公開するには、4ステップで済む。
- Pine Scriptエディタ右上の「発行」ボタンをクリック
- スクリプト名・説明・カテゴリ・タグを入力
- オープンソース(コードを公開)またはプロテクト(コードを非公開)を選択
- 「発行」を押すと審査なしで即座に公開される
公開後にコードを更新する場合は「バージョンを更新」から行う。重大なバグが見つかった場合、他のユーザーがすでに利用していても即時の強制更新はできない。公開スクリプトに誤ったシグナルロジックが含まれている場合、それを利用した第三者の損失について作者は法的責任を問われないが、コミュニティの信頼を失うリスクは存在する。
Pine Script v4→v5 マイグレーションガイド
TradingViewはv4スクリプトを開いた際に「自動変換」を提案するが、全ての変更を自動化できるわけではない。
| 変更カテゴリ | v4(旧) | v5(新) | 自動変換可否 |
|---|---|---|---|
| スクリプト宣言 | study() | indicator() | 自動変換あり |
| 外部データ取得 | security() | request.security() | 自動変換あり |
| テクニカル分析関数 | sma(), rsi() | ta.sma(), ta.rsi() | 自動変換あり |
| 数学関数 | abs(), sqrt() | math.abs(), math.sqrt() | 自動変換あり |
| 三項演算子 | iff(条件, a, b) | 条件 ? a : b | 手動対応必要 |
| 入力関数 | input() 単一 | input.float(), input.int() 等 | 手動対応必要 |
| 透明度指定 | transp= パラメーター | color.new(color.red, 50) | 手動対応必要 |
筆者が複数のv4スクリプトをv5へ移植した経験では、iff() の入れ子になった三項演算子への書き換えと、旧 input() のオーバーロード引数の整理に最も時間がかかった。自動変換後にエラーが残る場合は、これら3カテゴリから確認するとたいていの問題は片付く。
Pine Script / MQL5 / Python の比較と選択基準
用途・精度・学習コストの3軸で比べると、選択基準の輪郭が見えてくる。
| 比較軸 | Pine Script (v5/v6) | MQL5 (MetaTrader 5) | Python (backtesting.py等) |
|---|---|---|---|
| 難易度 | 低(初心者向け) | 高(C++類似、OOP必須) | 中(ライブラリ依存) |
| 実行環境 | TradingView専用・サーバーサイド | MetaTrader5・クライアントサイド | ローカルまたはクラウド |
| EA(自動売買ボット)作成 | 不可(ブローカー連携での注文送信のみ) | 可(完全自動化) | 可(外部API経由) |
| バックテスト精度 | Idealized bar data(楽観的) | Tick-by-tick シミュレーション | 柔軟(ライブラリ依存) |
| コミュニティ規模 | 15万本以上(TradingView) | MQL5マーケット | GitHub等 |
| 主な用途 | 可視化・シグナル生成・簡易検証 | 本番自動売買・高精度バックテスト | 研究・AI戦略・高度データ分析 |
MQL5でのEA開発をゼロから始めるなら、MQL4・MQL5プログラミング入門:EA開発基礎知識で言語仕様だけ先に把握しておくといい。
Idealized Bar Data 問題 — quantが見落とせない限界
Pine Scriptのバックテスト(Strategy Tester)が抱える構造的な問題は、実運用前に押さえておきたい。
Pine Scriptのバックテストは「1本足内の4本値(OHLC)を最良条件で処理する」設計になっている。具体的には、1本のローソク足の中でロングポジションのエントリーを計算する際に、その足の最安値(Low)でエントリーできたと仮定することがある。これを Idealized Bar Data(楽観的バーデータ) と呼ぶ。
MQL5のティックデータシミュレーションは1分足以下のレベルで価格変動を再現するため、同じロジックを実装してもバックテスト結果が 20〜30%乖離するケース がある(比較検証ベースの経験値。論文引用ではなく筆者による実装上の観察)。
この「楽観バイアス」を知らずに、Pine ScriptのStrategy Testerで好成績を収めたロジックをそのままMT5のEAに移植すると、実運用で大幅に性能が劣化する。Pine Scriptはあくまで「アイデアの素早い検証ツール」として使うにとどめ、本番運用前にはMQL5やPythonで高精度のバックテストを別途走らせることを勧めたい。
バックテストの過剰最適化(オーバーフィッティング)の見抜き方については、EAのカーブフィッティングと過剰最適化の回避手法で詳述している。
Claudeと会話しながらインジケータが作れる Hedgrow FX(Claude AIを搭載したFX支援ツール)はこちら。Pine ScriptのアイデアをAIとの対話で実装し、MQL5移植前の仮説検証を効率化できる。
よくある質問(FAQ)
Q: Pine Scriptは無料で使えますか? A: はい、TradingViewの無料プランでもPine Scriptの作成・実行は可能です。ただし無料プランではチャートに同時追加できるインジケーターが最大2本に制限されます。
Q: Pine ScriptでEA(自動売買ボット)は作れますか? A: 厳密な意味での「完全自動売買EA」は作れません。Pine ScriptのStrategyで注文ロジックを記述できますが、実際の注文送信にはTradingViewと提携しているブローカーへの接続設定が別途必要です。ポジションのフルオートメーションを実現したい場合はMQL5またはPython + ブローカーAPIの組み合わせが適切です。なお、Hedgrow FX ではClaudeと会話しながらインジケータが作れる。シグナルロジックの設計からコードの修正まで、AI対話形式で進めたい場合は参考にしてほしい。
Q: Pine ScriptとMQL5の違いは何ですか? A: 最大の違いは「用途と精度」です。Pine ScriptはTradingView専用で、チャートへの可視化とアイデア検証に優れています。MQL5はMetaTrader 5専用で、ティック精度のバックテストと本番自動売買が可能です。初学者にはPine Scriptが入りやすく、本番運用を目指すならMQL5への移行が現実的です。
Q: Pine Script v5とv6はどちらを学ぶべきですか? A: 2026年時点では v5を基礎として習得することを推奨します。コミュニティスクリプトの大多数がv5で書かれており、参考資料も豊富です。v6(2024年12月リリース)の新機能は型推論の強化が中心で、v5の文法と互換性があります。v5を習得した後、v6の差分を確認する流れが効率的です。
Q: バックテストで好成績でも実トレードで負けるのはなぜですか? A: 主な原因は3つです。(1) Idealized Bar Data問題(本文参照)による楽観的な約定条件の仮定、(2) スプレッドやスリッページの未反映、(3) 過去データへの過剰適合(オーバーフィッティング)。Pine ScriptのStrategy Testerは「このロジックが過去にどう機能したか」を把握するためのツールに過ぎず、将来の損益を保証するものでは一切ありません。
Q: Pine Scriptで自作したインジケーターは他のプラットフォームで使えますか? A: 使えません。Pine ScriptはTradingView専用言語であり、他のプラットフォームへの移植には再実装が必要です。MT5用にはMQL5、cTrader用にはcAlgoという固有言語があり、相互変換ツールも現時点では存在しません。
Claudeと会話しながらインジケータが作れる Hedgrow FX はこちら。本記事で解説したEMAクロスやRSI+MAフィルターをAIとの対話でカスタマイズし、自分だけの戦略を形にできる。
著者情報: 金融工学研究者・クオンツトレーダー(アルゴリズム取引ファンド経験者)監修 最終更新: 2026年07月
免責事項
本記事に掲載されているサンプルコードおよび解説は、教育目的での情報提供を目的としています。掲載されたインジケーターロジックおよびバックテスト結果は将来の収益を保証するものではなく、実際のトレードにおいては損失が発生するリスクがあります。FX取引は元本割れのリスクを伴う金融商品です。投資・取引の最終判断はご自身の責任において行ってください。本記事の内容に基づいたトレード行為に起因する損失について、筆者および当メディアは一切の責任を負いません。
