pine-debugger について
pine-debuggerは、TradingView上のPine Scriptコードを調査するスキルです。変数、ループ、リペイント、高時間足データ、描画オブジェクトのデバッグ手法と、よくあるエラーへの対処を扱います。
主な機能
- 01ラベルとテーブルによる値の確認
- 02ループとNAの診断
- 03リペイントの確認
- 04security呼び出しの検査
- 05UDTと描画状態の確認
活用シーン
- 01ループで停止するスクリプトを調べる
- 02予期しないNA値を追跡する
- 03過去とリアルタイムの挙動を比較する
- 04描画の欠落や位置ずれを診断する
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ traderspost/ pinescript-agents --skill pine-debuggerインストール後、Claude Code で/pine-debuggerを入力して読み込まれたか確認します。
詳細
Pine Script デバッガー
TradingView の不透明な環境で、Pine Script コードにデバッグツールを追加し、問題を解決することに特化しています。
⚠️ Pine Script v6 2025年のよくある落とし穴
重大:2025年3月の破壊的変更 — for ループの境界
for ループでは、境界が反復ごとに再評価されるようになりました。
推奨される修正:for...in ループを使用します:
// ✅ BEST: Use for...in for arrays (safe and clean)
for element in myArray
// Process element - safe even if array is modified
// ✅ BEST: Use for...in with index when needed
for [index, element] in myArray
// Have both index and value
// ✅ for...in works with matrices too
for row in myMatrix
// row is an array of the current row's values
// ✅ for...in works with maps (key-value pairs)
for [key, value] in myMap
// Iterate in insertion order
代替案:従来の for が必要な場合は、境界をキャッシュします:
// ❌ BUG: Infinite loop or unexpected behavior
var arr = array.new<int>()
for i = 0 to array.size(arr) // Re-evaluated each iteration!
array.push(arr, i) // Array grows, loop never ends
// ✅ FALLBACK: Cache the boundary before the loop
arrSize = array.size(arr)
for i = 0 to arrSize
array.push(arr, i)
デバッグ時の確認: ループが停止しないように見える場合は、for...in に切り替えるか、境界がキャッシュされていることを確認してください。
行継続エラー
最もよくある構文エラーです。2025年12月時点での更新ルール:
// ❌ ERROR: "end of line without line continuation"
text = condition ?
"value1" :
"value2"
// ✅ FIX: Keep ternary on one line
text = condition ? "value1" : "value2"
// ✅ Inside parentheses - any indentation now works (Dec 2025)
plot(series,
title="Plot",
color=color.blue)
// ⚠️ Outside parentheses - still requires non-multiple-of-4 indentation
condition = value1 > value2 and
value3 > value4 // Must use odd indentation (e.g., 5 spaces)
Bid/Ask 変数が NA を返す
// ❌ UNEXPECTED: Always na on most charts
bidValue = bid // Only works on 1T (tick) timeframe!
// ✅ DEBUG: Check timeframe
if barstate.islast
label.new(bar_index, high,
"Bid: " + (na(bid) ? "N/A (not 1T)" : str.tostring(bid)))
ローカルスコープ内の plot
// ❌ ERROR: "Cannot use 'plot' in local scope"
if condition
plot(value)
for i = 0 to 10
plot(close[i])
// ✅ FIX: Use conditional plotting at global scope
plot(condition ? value : na)
コアデバッグツールキット
1. ラベルベースの値インスペクター
// Debug label showing multiple values
if barstate.islast
debugText = "RSI: " + str.tostring(rsiValue, "#.##") + "\n"
debugText += "MA: " + str.tostring(maValue, "#.##") + "\n"
debugText += "Signal: " + (buySignal ? "BUY" : "NEUTRAL")
label.new(bar_index, high * 1.05, debugText,
style=label.style_label_down,
color=color.yellow,
textcolor=color.black)
2. テーブルベースの変数モニター
// Real-time variable monitor table
var table debugTable = table.new(position.top_right, 2, 10,
bgcolor=color.new(color.black, 80), border_width=1)
if barstate.islast
table.cell(debugTable, 0, 0, "Variable",
bgcolor=color.gray, text_color=color.white)
table.cell(debugTable, 1, 0, "Value",
bgcolor=color.gray, text_color=color.white)
table.cell(debugTable, 0, 1, "Bar Index", text_color=color.white)
table.cell(debugTable, 1, 1, str.tostring(bar_index), text_color=color.yellow)
table.cell(debugTable, 0, 2, "Close", text_color=color.white)
table.cell(debugTable, 1, 2, str.tostring(close, "#.####"), text_color=color.yellow)
table.cell(debugTable, 0, 3, "Signal Active", text_color=color.white)
sigColor = signalActive ? color.green : color.red
table.cell(debugTable, 1, 3, signalActive ? "YES" : "NO", text_color=sigColor)
3. ループ反復デバッガー
// Debug loop iteration (use for...in for safety)
var array<float> values = array.new<float>()
var int loopIterations = 0
loopIterations := 0
// ✅ PREFERRED: Use for...in (safe, no boundary issues)
for [idx, val] in values
loopIterations += 1
// Process val directly, idx available if needed
// Alternative: Traditional for with cached boundary
// boundarySize = array.size(values)
// for i = 0 to boundarySize - 1
// loopIterations += 1
if barstate.islast
label.new(bar_index, high,
"Loop ran " + str.tostring(loopIterations) + " times\n" +
"Array size: " + str.tostring(array.size(values)))
4. リペイント検出器
// Detect potential repainting
var bool repaintDetected = false
var float previousValue = na
if not barstate.isrealtime
if not na(previousValue) and previousValue != value[1]
repaintDetected := true
previousValue := value
if barstate.islast and repaintDetected
label.new(bar_index, high * 1.1, "⚠️ REPAINTING DETECTED",
style=label.style_label_down,
color=color.red,
textcolor=color.white)
5. NA 値トラッカー
// Debug na propagation
debugNa() =>
result = "NA Debug:\n"
result += "value1: " + (na(value1) ? "NA ❌" : "OK ✓") + "\n"
result += "value2: " + (na(value2) ? "NA ❌" : "OK ✓") + "\n"
result += "result: " + (na(result) ? "NA ❌" : "OK ✓")
result
if barstate.islast
label.new(bar_index, low, debugNa(),
style=label.style_label_up,
color=color.orange)
6. security 関数デバッガー
// Debug request.security() calls
[htfClose, htfTime] = request.security(syminfo.tickerid, "D",
[close, time],
lookahead=barmerge.lookahead_off)
if barstate.islast
secDebug = "HTF Debug:\n"
secDebug += "Close: " + str.tostring(htfClose, "#.##") + "\n"
secDebug += "Time: " + str.format_time(htfTime, "yyyy-MM-dd") + "\n"
secDebug += "Current TF: " + timeframe.period
label.new(bar_index, high, secDebug)
7. 入力のアクティブ状態デバッガー(2025年7月)
// Debug conditional inputs
showAdvanced = input.bool(false, "Show Advanced")
advValue = input.int(10, "Advanced Value",
active=showAdvanced, // July 2025 feature
tooltip="This input is conditional")
if barstate.islast
label.new(bar_index, high,
"showAdvanced: " + str.tostring(showAdvanced) + "\n" +
"advValue: " + str.tostring(advValue) + "\n" +
"(advValue active: " + str.tostring(showAdvanced) + ")")
UDT のデバッグ手法
1. UDT 状態インスペクター
// Debug UDT field values with a method
type Regression
int startTime
int endTime
int startBarIndex
int endBarIndex
float slope
float intercept
line centerLine = na
// Add debug method to UDT
method debug(Regression this, int yPos) =>
if barstate.islast and not na(this.startTime)
debugText = "Regression Debug:\n"
debugText += "startTime: " + str.format_time(this.startTime, "MM-dd HH:mm") + "\n"
debugText += "endTime: " + str.format_time(this.endTime, "MM-dd HH:mm") + "\n"
debugText += "startBar: " + str.tostring(this.startBarIndex) + "\n"
debugText += "endBar: " + str.tostring(this.endBarIndex) + "\n"
debugText += "slope: " + str.tostring(this.slope, "#.######") + "\n"
debugText += "intercept: " + str.tostring(this.intercept, "#.##") + "\n"
debugText += "hasLine: " + (na(this.centerLine) ? "NO ❌" : "YES ✓")
label.new(bar_index, yPos, debugText,
style=label.style_label_left,
color=color.blue,
textcolor=color.white)
// Use it
if debugMode
myRegression.debug(high)
2. UDT 配列インスペクターテーブル
// Debug all items in a UDT array
var table udtTable = table.new(position.bottom_left, 6, 12,
bgcolor=color.new(color.black, 80))
if debugMode and barstate.islast
// Header row
table.cell(udtTable, 0, 0, "#", text_color=color.gray)
table.cell(udtTable, 1, 0, "StartTime", text_color=color.gray)
table.cell(udtTable, 2, 0, "EndTime", text_color=color.gray)
table.cell(udtTable, 3, 0, "Slope", text_color=color.gray)
table.cell(udtTable, 4, 0, "HasLine", text_color=color.gray)
// Use for...in to iterate UDT array
row = 1
for [idx, reg] in regressions
if row < 11 // Limit rows
table.cell(udtTable, 0, row, str.tostring(idx), text_color=color.white)
table.cell(udtTable, 1, row, str.format_time(reg.startTime, "MM-dd"), text_color=color.yellow)
table.cell(udtTable, 2, row, str.format_time(reg.endTime, "MM-dd"), text_color=color.yellow)
slopeColor = reg.slope >= 0 ? color.green : color.red
table.cell(udtTable, 3, row, str.tostring(reg.slope, "#.####"), text_color=slopeColor)
lineStatus = na(reg.centerLine) ? "❌" : "✓"
table.cell(udtTable, 4, row, lineStatus, text_color=color.white)
row += 1
3. 時刻と bar_index の比較デバッガー
// Debug time-based coordinates vs bar_index
if debugMode and barstate.islast
currentTimeFormatted = str.format_time(time, "yyyy-MM-dd HH:mm")
barsAgo = bar_index - startBarIndex
debugText = "Coordinate Debug:\n"
debugText += "Current bar_index: " + str.tostring(bar_index) + "\n"
debugText += "Current time: " + currentTimeFormatted + "\n"
debugText += "─────────────────\n"
debugText += "Start bar_index: " + str.tostring(startBarIndex) + "\n"
debugText += "Start time: " + str.format_time(startTime, "yyyy-MM-dd HH:mm") + "\n"
debugText += "Bars ago: " + str.tostring(barsAgo) + "\n"
debugText += "─────────────────\n"
// Check if bar_index would hit 5000 limit
if barsAgo > 5000
debugText += "⚠️ WOULD EXCEED 5000 BAR LIMIT!\n"
debugText += "Use xloc.bar_time instead\n"
else
debugText += "✓ Within bar_index limit\n"
label.new(bar_index, high * 1.02, debugText,
style=label.style_label_down,
color=barsAgo > 5000 ? color.red : color.green)
4. 描画オブジェクト状態デバッガー
// Debug whether UDT drawing objects exist
method debugDrawings(Regression this) =>
status = "Drawing Status:\n"
status += "centerLine: " + (na(this.centerLine) ? "NULL ❌" : "EXISTS ✓") + "\n"
status += "upperLine: " + (na(this.upperLine) ? "NULL ❌" : "EXISTS ✓") + "\n"
status += "lowerLine: " + (na(this.lowerLine) ? "NULL ❌" : "EXISTS ✓") + "\n"
status += "fill: " + (na(this.fill) ? "NULL ❌" : "EXISTS ✓")
status
if debugMode and barstate.islast
for [i, reg] in regressions
if i == 0 // Just debug first one
label.new(bar_index, low, reg.debugDrawings(),
style=label.style_label_up,
color=color.purple)
5. UDT 作成フローデバッガー
// Track when UDTs are created
var int udtCreationCount = 0
var array<string> creationLog = array.new<string>()
// When creating new UDT
if shouldCreateNew
udtCreationCount += 1
logEntry = "Created #" + str.tostring(udtCreationCount) + " at bar " + str.tostring(bar_index)
array.push(creationLog, logEntry)
newReg = Regression.new(
startTime = time,
startBarIndex = bar_index,
// ...
)
array.push(regressions, newReg)
// Display creation log
if debugMode and barstate.islast
logText = "UDT Creation Log:\n"
logText += "Total created: " + str.tostring(udtCreationCount) + "\n"
logText += "─────────────────\n"
// Show last 5 entries using for...in
startIdx = math.max(0, array.size(creationLog) - 5)
for [i, entry] in creationLog
if i >= startIdx
logText += entry + "\n"
label.new(bar_index - 10, high, logText,
style=label.style_label_right,
color=color.teal)
6. xloc.bar_time と xloc.bar_index の検証ツール
// Verify time coordinates work correctly
if debugMode
// Draw test lines with both methods
if barstate.islast
testStartBar = bar_index - 100
testStartTime = time[100]
// bar_index method (may fail if >5000 bars back)
testLine1 = line.new(testStartBar, close[100], bar_index, close,
xloc=xloc.bar_index,
color=color.red,
width=2)
// bar_time method (unlimited lookback)
testLine2 = line.new(testStartTime, close[100] * 0.99, time, close * 0.99,
xloc=xloc.bar_time,
color=color.green,
width=2)
label.new(bar_index, low * 0.98,
"Red: xloc.bar_index\nGreen: xloc.bar_time\n(Should overlap if working)",
style=label.style_label_up,
color=color.yellow)
よくある問題と解決策
1. 無限ループ(2025年3月)
症状: スクリプトが停止する、またはタイムアウトする
// ❌ CAUSE: Dynamic boundary grows during loop
for i = 0 to array.size(arr)
array.push(arr, i)
// ✅ FIX: Cache boundary
size = array.size(arr)
for i = 0 to size
array.push(arr, i)
2. 予期しない NA 値
症状: 計算結果が予期せず na になる
// Debug NA chain
if na(finalResult)
// Check each component
if na(input1)
label.new(bar_index, high, "input1 is NA")
else if na(intermediate)
label.new(bar_index, high, "intermediate is NA")
else
label.new(bar_index, high, "Unknown NA source")
3. Security 関数の問題
症状: 上位時間足から誤った値が返される
// ✅ Correct security() usage
htfValue = request.security(syminfo.tickerid, "D", close,
gaps=barmerge.gaps_off,
lookahead=barmerge.lookahead_off) // Prevent future leak
4. 過去データとリアルタイムの不一致
症状: ストラテジーがバックテストでは動作するが、ライブでは失敗する
// Debug historical vs realtime
if barstate.islast
label.new(bar_index, high,
"ishistorical: " + str.tostring(barstate.ishistory) + "\n" +
"isrealtime: " + str.tostring(barstate.isrealtime) + "\n" +
"isconfirmed: " + str.tostring(barstate.isconfirmed))
5. 文字列が長すぎる(2025年8月以前)
症状: 文字列の切り詰めエラー
// August 2025: Limit increased to 40,960 characters
// If still hitting limits, chunk the string
longString = ""
for i = 0 to 100
if str.length(longString) < 40000 // Leave buffer
longString += "data" + str.tostring(i) + "\n"
デバッグモードの実装
// Master debug toggle
debugMode = input.bool(false, "🔧 Debug Mode", group="Debug")
// Conditional debug elements
var table debugTable = na
if debugMode
debugTable := table.new(position.top_right, 2, 5,
bgcolor=color.new(color.black, 80))
if debugMode and barstate.islast
table.cell(debugTable, 0, 0, "Debug Info", text_color=color.white)
// ... add debug info
// Debug plots (only when debug mode on)
plot(debugMode ? debugValue : na, "Debug Value", color.yellow)
デバッグワークフロー
-
症状を特定する
- コンパイルエラーですか?
- 実行時エラーですか?
- 値が誤っていますか?
- パフォーマンスの問題ですか?
-
2025年の破壊的変更を確認する
- for ループの境界(2025年3月)
- 行継続のルール
- Bid/Ask の利用可否
-
デバッグポイントを追加する
- 重要な計算箇所にラベルを追加する
- 変数の監視用にテーブルを追加する
- 中間値をプロットする
-
実行を追跡する
- 計算の流れをたどる
- 条件の評価を確認する
- 状態の変化を監視する
-
エッジケースをテストする
- 最初のバー(bar_index == 0)
- NA 値
- リアルタイムと過去データ
- 異なる時間足
-
後片付けをする
- デバッグコードを
if debugModeで囲む - または公開前に削除する
- デバッグコードを
クイックリファレンス:エラーメッセージ
| エラー | 考えられる原因 | 修正方法 |
|---|---|---|
| "end of line without line continuation" | 三項演算子が分割されている、またはインデントが正しくない | 三項演算子を1行に収め、5スペースでインデントする |
| "Cannot use 'plot' in local scope" | if/for/関数の内側に plot() がある | plot(condition ? value : na) を使用する |
| "Loop too long" | for ループの境界が動的 | 境界をキャッシュするか、for...in を使用する |
| "String is too long" | 文字列が 40,960 文字を超えている | 文字列を分割するか、内容を減らす |
| どこもかしこも "na" | NA の伝播 | 各ステップで NA チェックを追加する |
| 描画が表示されない | bar_index が 5000 本より前 | 代わりに xloc.bar_time を使用する |
| UDT の線が na | line.new() の後に割り当てていない | := を使用して描画を UDT フィールドに割り当てる |
UDT 固有のデバッグチェックリスト
- イベント発生時(後からではなく)に時刻の値を取得していますか?
- 描画と計算のために
timeとbar_indexの両方を保存していますか? - 描画オブジェクトで
xloc.bar_timeを使用していますか? -
draw()の前にdelete()を呼び出していますか? - UDT メソッドで代入に
this.field :=を使用していますか? - UDT 配列を
for...inで反復していますか?
TradingView の環境は不透明です。必ず十分にテストし、複数のデバッグ方法を提示してください。
コメント
Markdown 対応