From pinescript
Optimizes Pine Script for performance and user experience on TradingView. Use when improving script speed, UI organization, or visual appeal.
How this skill is triggered — by the user, by Claude, or both
Slash command
/pinescript:pine-optimizerThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Specialized in enhancing script performance, user experience, and visual presentation on TradingView.
Specialized in enhancing script performance, user experience, and visual presentation on TradingView.
active Parameter - UX Game ChangerCreate conditional input visibility for cleaner 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)
More visual options without custom drawing:
// 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)
No longer limited to 550 scopes - enables more complex, modular code:
// 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
for...in for CollectionsThe preferred and safest way to iterate over collections:
// ✅ 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
FALLBACK: Cache boundary if traditional for is required:
// ❌ 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
Build more comprehensive info panels:
// 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
The single biggest optimization you can make is using UDT-first architecture. This eliminates parallel arrays, simplifies drawing management, and removes bar_index clamping code.
// ❌ 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)
// ❌ 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!
// ❌ 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)
| Metric | Parallel Arrays | UDT Architecture |
|---|---|---|
| Array declarations | 6-10 | 1 |
| Drawing code blocks | 5+ (duplicated) | 1 method |
| Clamping code | 4+ locations | 0 |
| Lines of code | 400+ | ~150 |
| Bug risk | High (sync issues) | Low |
| Historical lookback | 5000 bars max | Unlimited |
// 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
...
)
// ❌ 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))
// ❌ 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])
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)
// ❌ 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
// 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
// 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)
// 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)
// 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))
xloc.bar_time for drawings (eliminates clamping)var for values that don't change per baractive parameter for conditional inputslinestyle parameter for visual hierarchy// 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)
| Issue | Before | After | Impact |
|---|---|---|---|
| Parallel arrays | 6+ arrays to sync | 1 UDT array | 80% less code |
| bar_index drawing | Clamping + 5000 limit | xloc.bar_time | Unlimited lookback |
| Drawing code | Duplicate blocks | UDT methods | DRY, maintainable |
| Repeated SMA | ta.sma(close,20) x5 | Cache in variable | 5x faster |
| Multiple security() | 4 separate calls | 1 tuple call | 4x faster |
| Loop boundary | array.size(arr) in loop | Cache before loop | Prevents hang |
| All inputs visible | Cluttered UI | Use active param | Clean UX |
| One line style | All solid | solid/dashed/dotted | Better hierarchy |
The top 3 optimizations (UDT-related) provide the biggest gains. Balance optimization with readability. Don't over-optimize at the expense of maintainability.
npx claudepluginhub traderspost/pinescript-agentsWrites production-quality Pine Script v6 code for TradingView, implementing indicators and strategies with latest 2025 syntax and breaking changes.
Writes production-quality Pine Script v6 code for TradingView indicators and strategies, following TradingView guidelines and best practices.
Builds TradingView Pine Script v6 indicators in a consistent house style with synthwave dashboard aesthetics, faithful math, and v6 compliance. Handles porting from Python or other Pine scripts.