Skill v1.0.0
currentAutomated scan100/100name: pine-optimizer description: Optimizes Pine Script for performance and user experience. Use when improving speed, UI organization, or visual appeal. allowed-tools: Read, Edit, Grep, Glob metadata: version: "1.0.0"
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 selectedtooltip="Only available when MA Type is 'Custom'")// Advanced mode patternshowAdvanced = 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 visualsplot(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 displayshowProjections = 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 errorstype ComplexTypefloat value1float value2array<float> historymethod process(ComplexType this) =>if this.value1 > 0if this.value2 > 0for i = 0 to array.size(this.history) - 1if 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 neededfor [index, element] in myArray// Have both index and value// ✅ Works with matrices (iterates rows as arrays)for row in myMatrixfor 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 iterationfor i = 0 to array.size(arr) - 1// Can cause infinite loop if array modified// ✅ FALLBACK: Cache boundary if you need traditional forarrSize = 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 reportsreport = "═══════════════════════════════\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 itemarray.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 Regressionfloat slopefloat interceptfloat stdDevint startTime // Use TIME, not bar_index!int endTimeint startBarIndex // Keep for Y calculationsint endBarIndexline centerLine = navar array<Regression> regressions = array.new<Regression>()// One push, all data togethernewReg = 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 rangeif bar_index - startBar > 5000leftBar := 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 drawingif showOriginalif not na(origCenterLine)line.delete(origCenterLine)origCenterLine := line.new(...)// ... 20 more lines// Block for live regression drawing (duplicate code!)if showLiveif 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 oldstartY = 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 chainedRegressionsreg.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 occurvar int eventStartTime = navar int eventStartBar = naif eventDetectedeventStartTime := time // For xloc.bar_time drawingeventStartBar := bar_index // For Y calculation (slope * bar + intercept)// Later, create UDT with bothnewItem = MyType.new(startTime = eventStartTime, // Drawing coordinatestartBarIndex = eventStartBar, // Calculation coordinate...)
Core Optimization Areas
1. Calculation Caching
// ❌ BEFORE: Repeated calculationsplot(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 timessma20 = ta.sma(close, 20)sma50 = ta.sma(close, 50)bullish = sma20 > sma50plot(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 iterationfor i = 0 to array.size(arr) - 1val = 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 neededfor [i, element] in arr// Have both index and value// ✅ FASTEST: Use built-in array methods when possibletotal = array.sum(arr)maxVal = array.max(arr)avgVal = array.avg(arr)
4. Conditional Logic Optimization
// ❌ SLOW: All conditions evaluatedsignal = cond1 and cond2 and cond3 and expensiveCalc()// ✅ FAST: Short-circuit with cheap checks firstsignal = cond1 // Cheapest firstsignal := signal and cond2signal := signal and cond3signal := 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 palettevar color BULL = color.new(#26a69a, 0) // Teal greenvar color BEAR = color.new(#ef5350, 0) // Coral redvar color BULL_BG = color.new(#26a69a, 85) // Transparentvar color BEAR_BG = color.new(#ef5350, 85)var color NEUTRAL = color.new(#787b86, 0) // Gray// Gradient based on strengthstrength = ta.rsi(close, 14)gradientColor = color.from_gradient(strength, 30, 70, BEAR, BULL)// Dark mode friendly tablevar table infoTable = table.new(position.top_right, 2, 5,bgcolor=color.new(#131722, 10), // TradingView dark bgborder_color=color.new(#363a45, 0),border_width=1)
3. Responsive Plot Styling with Line Styles
// Use new linestyle parameter for visual hierarchyma = ta.ema(close, maLength)maPlot = plot(ma, "MA", color.blue, linewidth=2,linestyle=plot.linestyle_solid) // Primary: solid// Secondary indicators: dashedplot(upperBand, "Upper", color.gray,linestyle=plot.linestyle_dashed)plot(lowerBand, "Lower", color.gray,linestyle=plot.linestyle_dashed)// Projections/targets: dottedplot(showTargets ? target : na, "Target", color.green,linestyle=plot.linestyle_dotted)
4. Smart Alert Messages
// Comprehensive alert with all relevant infobuildAlert(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 += "═══════════════════"msgalertcondition(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 viewingcompactMode = input.bool(false, "📱 Compact Mode",group="Display",tooltip="Enable for better mobile viewing")// Adjust based on modeplotWidth = compactMode ? 1 : 2labelSize = compactMode ? size.tiny : size.small// Conditional table displayif not compactMode// Full table with detailstable.cell(infoTable, 0, 0, "Full Statistics", text_color=color.white)// ... more cellselse// Minimal displaytable.cell(infoTable, 0, 0, bullish ? "↑" : "↓",text_color=bullish ? BULL : BEAR,text_size=size.large)
Memory Optimization
// Use var for persistent valuesvar float highestHigh = navar int barsSinceSignal = 0// Limit array sizesvar array<float> recentPrices = array.new<float>(100)if barstate.isconfirmedarray.push(recentPrices, close)if array.size(recentPrices) > 100array.shift(recentPrices)// Clear unused dataif newDayarray.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.