pine-optimizer について
pine-optimizerは、TradingViewのスクリプトを改善するPine Script向けスキルです。コード構成や計算・ループの効率化に加え、入力項目やプロットの見せ方も扱います。
主な機能
- 01UDTによるデータ構成
- 02反復計算のキャッシュ
- 03security() 呼び出しの最適化
- 04コレクションの安全な反復
- 05条件付き入力とプロットスタイル
活用シーン
- 01並列配列をUDTに置き換える
- 02指標計算の重複を減らす
- 03ループ境界とsecurity()の使い方を確認する
- 04スクリプトの入力項目とプロットを読みやすくする
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ traderspost/ pinescript-agents --skill pine-optimizerインストール後、Claude Code で/pine-optimizerを入力して読み込まれたか確認します。
詳細
Pine Script オプティマイザー
TradingView でのスクリプトのパフォーマンス、ユーザーエクスペリエンス、視覚的な表現の向上に特化しています。
Pine Script v6 2025 年最適化機能
2025 年 7 月:入力の active パラメーター — UX を一変させる機能
条件に応じて入力項目の表示を切り替え、すっきりした UI を実現します。
// ✅ BEFORE: All inputs always visible (cluttered)
maType = input.string("EMA", "MA Type", options=["SMA", "EMA", "Custom"])
customLength = input.int(20, "Custom Length") // Always visible
// ✅ AFTER: Conditional visibility (clean UX)
maType = input.string("EMA", "MA Type", options=["SMA", "EMA", "Custom"])
customLength = input.int(20, "Custom Length",
active=(maType == "Custom"), // Only shown when Custom selected
tooltip="Only available when MA Type is 'Custom'")
// Advanced mode pattern
showAdvanced = input.bool(false, "Show Advanced Settings", group="Settings")
advSetting1 = input.int(10, "Advanced 1", group="Advanced", active=showAdvanced)
advSetting2 = input.float(1.5, "Advanced 2", group="Advanced", active=showAdvanced)
advSetting3 = input.bool(true, "Advanced 3", group="Advanced", active=showAdvanced)
2025 年 9 月:プロットのラインスタイル
カスタム描画なしで、より多くの視覚的な表現が可能になります。
// Use built-in line styles for cleaner visuals
plot(sma20, "SMA 20", color.blue, linestyle=plot.linestyle_solid)
plot(sma50, "SMA 50", color.red, linestyle=plot.linestyle_dashed)
plot(support, "Support", color.green, linestyle=plot.linestyle_dotted)
// Combine with conditional display
showProjections = input.bool(true, "Show Projections")
plot(showProjections ? projection : na, "Projection",
color.new(color.yellow, 50),
linestyle=plot.linestyle_dotted)
2025 年 2 月:スコープ数の上限撤廃
スコープ数が 550 に制限されなくなり、より複雑でモジュール化されたコードを記述できます。
// Now possible: deeply nested logic without scope errors
type ComplexType
float value1
float value2
array<float> history
method process(ComplexType this) =>
if this.value1 > 0
if this.value2 > 0
for i = 0 to array.size(this.history) - 1
if array.get(this.history, i) > 0
// Deep nesting now allowed
// Previously would hit 550 scope limit
2025 年 3 月:コレクションには for...in を使用
コレクションを反復処理する、推奨される最も安全な方法です。
// ✅ BEST: Use for...in for arrays (safest, cleanest)
for element in myArray
// Process element directly - no boundary issues
// ✅ BEST: Use for...in with index when needed
for [index, element] in myArray
// Have both index and value
// ✅ Works with matrices (iterates rows as arrays)
for row in myMatrix
for value in row
// Process each value
// ✅ Works with maps (key-value pairs in insertion order)
for [key, value] in myMap
// Process key-value pair
代替手段:従来の for が必要な場合は、境界値をキャッシュします。
// ❌ SLOW/DANGEROUS: Re-evaluates array.size() every iteration
for i = 0 to array.size(arr) - 1
// Can cause infinite loop if array modified
// ✅ FALLBACK: Cache boundary if you need traditional for
arrSize = array.size(arr)
for i = 0 to arrSize - 1
// process
2025 年 8 月:文字列の長さが延長(40,960 文字)
より詳細な情報パネルを作成できます。
// Now possible: detailed multi-section reports
report = "═══════════════════════════════\n"
report += " STRATEGY REPORT \n"
report += "═══════════════════════════════\n\n"
report += "PERFORMANCE METRICS\n"
report += "───────────────────\n"
report += "Win Rate: " + str.tostring(winRate, "#.##") + "%\n"
report += "Profit Factor: " + str.tostring(pf, "#.##") + "\n"
// ... can now include much more detail
⚠️ 重要:UDT を中心としたコード構成
実行できる最大の最適化は、UDT を中心としたアーキテクチャを採用することです。 並列配列をなくし、描画管理を簡素化し、bar_index のクランプ処理を不要にします。
改善前と改善後:並列配列と UDT
// ❌ BEFORE: 6+ parallel arrays (hard to maintain, error-prone)
var array<float> slopes = array.new<float>()
var array<float> intercepts = array.new<float>()
var array<float> stdDevs = array.new<float>()
var array<int> startBars = array.new<int>()
var array<int> endBars = array.new<int>()
var array<line> centerLines = array.new<line>()
// Must keep ALL arrays in sync - easy to have bugs!
// Push to ALL arrays when adding item
array.push(slopes, newSlope)
array.push(intercepts, newIntercept)
array.push(stdDevs, newStdDev)
array.push(startBars, newStartBar)
array.push(endBars, newEndBar)
// Forgot to push to centerLines? Bug!
// ✅ AFTER: Single UDT array (clean, self-documenting)
type Regression
float slope
float intercept
float stdDev
int startTime // Use TIME, not bar_index!
int endTime
int startBarIndex // Keep for Y calculations
int endBarIndex
line centerLine = na
var array<Regression> regressions = array.new<Regression>()
// One push, all data together
newReg = Regression.new(slope=newSlope, intercept=newIntercept, ...)
array.push(regressions, newReg)
パフォーマンス向上:xloc.bar_time によりクランプ処理を排除
// ❌ BEFORE: Clamping code everywhere (slow, complex)
leftBar = math.max(0, bar_index - startOffset) // Clamp to valid range
if bar_index - startBar > 5000
leftBar := bar_index - 5000 // Historical buffer limit!
line.set_x1(myLine, leftBar)
// ✅ AFTER: xloc.bar_time has NO limit - just draw!
line.new(startTime, y1, endTime, y2, xloc=xloc.bar_time)
// No clamping needed! Works for ANY historical bar!
DRY な描画コードを実現する UDT メソッド
// ❌ BEFORE: Repeated drawing blocks (~50+ lines each)
// Block for original regression drawing
if showOriginal
if not na(origCenterLine)
line.delete(origCenterLine)
origCenterLine := line.new(...)
// ... 20 more lines
// Block for live regression drawing (duplicate code!)
if showLive
if not na(liveCenterLine)
line.delete(liveCenterLine)
liveCenterLine := line.new(...)
// ... 20 more duplicate lines
// ✅ AFTER: Single method, reuse everywhere (~30 lines total)
method draw(Regression this, color baseColor, int width) =>
this.delete() // Clean up old
startY = this.calculateY(this.startBarIndex)
endY = this.calculateY(this.endBarIndex)
this.centerLine := line.new(this.startTime, startY, this.endTime, endY,
xloc=xloc.bar_time, color=baseColor, width=width)
// Use anywhere - no duplication!
originalRegression.draw(origColor, 2)
liveRegression.draw(liveColor, 2)
for reg in chainedRegressions
reg.draw(chainedColor, 1)
UDT 最適化の指標
| 指標 | 並列配列 | UDT アーキテクチャ |
|---|---|---|
| 配列の宣言 | 6~10 | 1 |
| 描画コードブロック | 5 以上(重複) | 1 メソッド |
| クランプ処理のコード | 4 か所以上 | 0 |
| コード行数 | 400 以上 | 約 150 |
| バグのリスク | 高(同期の問題) | 低 |
| 過去データの参照範囲 | 最大 5000 バー | 無制限 |
UDT での時刻取得パターン
// ALWAYS capture BOTH time and bar_index when events occur
var int eventStartTime = na
var int eventStartBar = na
if eventDetected
eventStartTime := time // For xloc.bar_time drawing
eventStartBar := bar_index // For Y calculation (slope * bar + intercept)
// Later, create UDT with both
newItem = MyType.new(
startTime = eventStartTime, // Drawing coordinate
startBarIndex = eventStartBar, // Calculation coordinate
...
)
主な最適化分野
1. 計算のキャッシュ
// ❌ BEFORE: Repeated calculations
plot(ta.sma(close, 20) > ta.sma(close, 50) ? high : low)
bgcolor(ta.sma(close, 20) > ta.sma(close, 50) ? color.green : color.red)
// ✅ AFTER: Cache once, use many times
sma20 = ta.sma(close, 20)
sma50 = ta.sma(close, 50)
bullish = sma20 > sma50
plot(bullish ? high : low)
bgcolor(bullish ? color.new(color.green, 90) : color.new(color.red, 90))
2. セキュリティ呼び出しの最適化
// ❌ BEFORE: Multiple security() calls (expensive)
htfClose = request.security(syminfo.tickerid, "D", close)
htfHigh = request.security(syminfo.tickerid, "D", high)
htfLow = request.security(syminfo.tickerid, "D", low)
htfVolume = request.security(syminfo.tickerid, "D", volume)
// ✅ AFTER: Single tuple call
[htfClose, htfHigh, htfLow, htfVolume] = request.security(
syminfo.tickerid, "D",
[close, high, low, volume])
3. ループの最適化(for...in を使用)
// ❌ SLOW/DANGEROUS: Boundary re-evaluated each iteration
for i = 0 to array.size(arr) - 1
val = array.get(arr, i)
// process
// ✅ BEST: Use for...in (safe, clean, preferred)
for element in arr
// Process element directly - no boundary issues
// ✅ BEST: With index when needed
for [i, element] in arr
// Have both index and value
// ✅ FASTEST: Use built-in array methods when possible
total = array.sum(arr)
maxVal = array.max(arr)
avgVal = array.avg(arr)
4. 条件ロジックの最適化
// ❌ SLOW: All conditions evaluated
signal = cond1 and cond2 and cond3 and expensiveCalc()
// ✅ FAST: Short-circuit with cheap checks first
signal = cond1 // Cheapest first
signal := signal and cond2
signal := signal and cond3
signal := signal and expensiveCalc() // Only if others pass
ユーザーエクスペリエンスの最適化
1. Active パラメーターによる入力項目の整理
// Professional input structure with conditional visibility
// === Main Settings ===
strategyMode = input.string("Trend Following", "Strategy Mode",
options=["Trend Following", "Mean Reversion", "Custom"],
group="Strategy")
// === Trend Following Settings (conditional) ===
tfLength = input.int(20, "Trend Length",
group="Trend Following",
active=(strategyMode == "Trend Following"),
tooltip="Period for trend detection")
tfMultiplier = input.float(2.0, "Trend Multiplier",
group="Trend Following",
active=(strategyMode == "Trend Following"))
// === Mean Reversion Settings (conditional) ===
mrLength = input.int(14, "Reversion Length",
group="Mean Reversion",
active=(strategyMode == "Mean Reversion"))
mrThreshold = input.float(2.0, "Reversion Threshold",
group="Mean Reversion",
active=(strategyMode == "Mean Reversion"))
// === Visual Settings ===
showPlots = input.bool(true, "Show Plots", group="Visual")
plotColor = input.color(color.blue, "Plot Color",
group="Visual",
active=showPlots) // Only show when plots enabled
2. プロフェッショナルな配色
// Modern, accessible color palette
var color BULL = color.new(#26a69a, 0) // Teal green
var color BEAR = color.new(#ef5350, 0) // Coral red
var color BULL_BG = color.new(#26a69a, 85) // Transparent
var color BEAR_BG = color.new(#ef5350, 85)
var color NEUTRAL = color.new(#787b86, 0) // Gray
// Gradient based on strength
strength = ta.rsi(close, 14)
gradientColor = color.from_gradient(strength, 30, 70, BEAR, BULL)
// Dark mode friendly table
var table infoTable = table.new(position.top_right, 2, 5,
bgcolor=color.new(#131722, 10), // TradingView dark bg
border_color=color.new(#363a45, 0),
border_width=1)
3. ラインスタイルを使用したレスポンシブなプロットのスタイル設定
// Use new linestyle parameter for visual hierarchy
ma = ta.ema(close, maLength)
maPlot = plot(ma, "MA", color.blue, linewidth=2,
linestyle=plot.linestyle_solid) // Primary: solid
// Secondary indicators: dashed
plot(upperBand, "Upper", color.gray,
linestyle=plot.linestyle_dashed)
plot(lowerBand, "Lower", color.gray,
linestyle=plot.linestyle_dashed)
// Projections/targets: dotted
plot(showTargets ? target : na, "Target", color.green,
linestyle=plot.linestyle_dotted)
4. スマートなアラートメッセージ
// Comprehensive alert with all relevant info
buildAlert(direction, price, strength) =>
msg = "🔔 " + syminfo.ticker + " ALERT\n"
msg += "═══════════════════\n"
msg += "Direction: " + direction + "\n"
msg += "Price: $" + str.tostring(price, "#,###.##") + "\n"
msg += "Strength: " + str.tostring(strength, "#.#") + "/10\n"
msg += "Time: " + str.format_time(time, "yyyy-MM-dd HH:mm") + "\n"
msg += "═══════════════════"
msg
alertcondition(buySignal,
"Buy Signal",
buildAlert("LONG", close, signalStrength))
パフォーマンスチェックリスト
アーキテクチャ(最初に確認!)
- 複雑なデータ構造には、UDT を中心とした構成を使用する
- 並列配列を 1 つの UDT 配列に置き換える
- 描画には
xloc.bar_timeを使用する(クランプ処理を排除) - 各座標の時刻と bar_index を保存する
- ロジックをカプセル化するため、UDT にメソッドを追加する
必須の最適化
- 繰り返し行われるすべての計算をキャッシュする
- security() 呼び出しをタプルにまとめる
- ループの境界値をキャッシュする(2025 年 3 月の重要事項)
- コストの低い条件を、コストの高い条件より先に記述する
- バーごとに変化しない値には
varを使用する
UX の最適化(2025 年の機能)
- 条件に応じた入力項目には
activeパラメーターを使用する - 関連する入力項目を論理的にグループ化する
- 複雑な入力項目にツールチップを追加する
- 視覚的な階層を表現するため、新しい
linestyleパラメーターを使用する - モバイルに適したコンパクトモードのオプションを実装する
視覚的な最適化
- 一貫性のある配色を使用する
- 強さを表すグラデーションカラーを実装する
- 適切なプロットの線幅を使用する
- 背景色に透明度を加える
- ダークモードでも見やすいテーブルにする
モバイル向け最適化
// Compact mode for mobile viewing
compactMode = input.bool(false, "📱 Compact Mode",
group="Display",
tooltip="Enable for better mobile viewing")
// Adjust based on mode
plotWidth = compactMode ? 1 : 2
labelSize = compactMode ? size.tiny : size.small
// Conditional table display
if not compactMode
// Full table with details
table.cell(infoTable, 0, 0, "Full Statistics", text_color=color.white)
// ... more cells
else
// Minimal display
table.cell(infoTable, 0, 0, bullish ? "↑" : "↓",
text_color=bullish ? BULL : BEAR,
text_size=size.large)
メモリの最適化
// Use var for persistent values
var float highestHigh = na
var int barsSinceSignal = 0
// Limit array sizes
var array<float> recentPrices = array.new<float>(100)
if barstate.isconfirmed
array.push(recentPrices, close)
if array.size(recentPrices) > 100
array.shift(recentPrices)
// Clear unused data
if newDay
array.clear(intradayData)
最適化のクイックウィン
| 問題 | 最適化前 | 最適化後 | 効果 |
|---|---|---|---|
| 並列配列 | 同期が必要な配列が6個以上 | UDT配列1個 | コード量を80%削減 |
| bar_index による描画 | クランプ処理+5000の上限 | xloc.bar_time | ルックバックの上限なし |
| 描画コード | 重複するブロック | UDTメソッド | DRYで保守しやすい |
| SMAの繰り返し | ta.sma(close,20) x5 | 変数にキャッシュ | 5倍高速 |
| security() の複数呼び出し | 個別に4回呼び出し | タプルで1回呼び出し | 4倍高速 |
| ループ境界 | ループ内でarray.size(arr) | ループ前にキャッシュ | ハングを防止 |
| すべての入力を表示 | UIが煩雑 | activeパラメーターを使用 | すっきりしたUX |
| 線のスタイルが1種類 | すべて実線 | 実線/破線/点線 | 階層が明確 |
最も効果が大きい最適化は、上位3つのUDT関連のものです。 最適化と読みやすさのバランスを取りましょう。保守性を犠牲にしてまで過剰に最適化しないでください。
コメント
Markdown 対応