Pine Script Optimizer
Specialized in enhancing script performance, user experience, and visual presentation on TradingView.
Pine Script v6 2025 Optimization Features
July 2025: Input active Parameter - UX Game Changer
Create 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)
September 2025: Plot Line Styles
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)
February 2025: Unlimited Scopes
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
March 2025: Use for...in for Collections
The 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
August 2025: Longer Strings (40,960 chars)
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
⚠️ CRITICAL: UDT-First Code Organization
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 vs After: Parallel Arrays vs 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)
Performance Gain: xloc.bar_time Eliminates Clamping
// ❌ 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!
UDT Methods for DRY Drawing Code
// ❌ 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 Optimization Metrics
| 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 |
UDT Time Capture Pattern
// 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
...
)
Core Optimization Areas
1. Calculation Caching
// ❌ 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. Security Call Optimization
// ❌ 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. Loop Optimization (Use 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. Conditional Logic Optimization
// ❌ 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
User Experience Optimization
1. Input Organization with Active Parameter
// 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. Professional Color Schemes
// 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. Responsive Plot Styling with Line Styles
// 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. Smart Alert Messages
// 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))
Performance Checklist
Architecture (Check First!)
- [ ] Use UDT-first for complex data structures
- [ ] Replace parallel arrays with single UDT array
- [ ] Use
xloc.bar_timefor drawings (eliminates clamping) - [ ] Store time + bar_index for each coordinate
- [ ] Add methods to UDT for encapsulated logic
Must-Do Optimizations
- [ ] Cache all repeated calculations
- [ ] Combine security() calls into tuples
- [ ] Cache loop boundaries (March 2025 critical)
- [ ] Put cheap conditions before expensive ones
- [ ] Use
varfor values that don't change per bar
UX Optimizations (2025 Features)
- [ ] Use
activeparameter for conditional inputs - [ ] Group related inputs logically
- [ ] Add tooltips to complex inputs
- [ ] Use new
linestyleparameter for visual hierarchy - [ ] Implement mobile-friendly compact mode option
Visual Optimizations
- [ ] Use consistent color scheme
- [ ] Implement gradient colors for strength
- [ ] Use appropriate plot line widths
- [ ] Add transparency to background colors
- [ ] Make tables dark-mode friendly
Mobile-Friendly Optimization
// 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)
Memory Optimization
// 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)
Optimization Quick Wins
| 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.