关于 pine-optimizer
pine-optimizer 是面向 Pine Script 作者的技能,用于改进 TradingView 脚本。内容涵盖代码结构、计算与循环效率,以及输入项和图表呈现。
主要功能
- 01采用 UDT 组织数据
- 02缓存重复计算
- 03优化 security() 调用
- 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 体验革新
创建条件输入项可见性,让界面更整洁:
// ✅ 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!
使用 UDT 方法编写 DRY 绘图代码
// ❌ 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 根 K 线 | 不限 |
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. Security 调用优化
// ❌ 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
- 用单个 UDT 数组替代并行数组
- 对绘图使用
xloc.bar_time(无需限制) - 为每个坐标存储时间和 bar_index
- 在 UDT 中添加方法以封装逻辑
必做优化
- 缓存所有重复计算
- 将 security() 调用合并为元组
- 缓存循环边界(2025 年 3 月的关键要求)
- 将开销低的条件放在开销高的条件前面
- 对每根 K 线都不会变化的值使用
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 个以上的数组 | 1 个 UDT 数组 | 代码减少 80% |
| bar_index 绘图 | 需进行钳制,且受 5000 的限制 | xloc.bar_time | 回溯范围不受限 |
| 绘图代码 | 重复代码块 | UDT 方法 | 遵循 DRY 原则,易于维护 |
| 重复计算 SMA | ta.sma(close,20) x5 | 缓存在变量中 | 快 5 倍 |
| 多次调用 security() | 分别调用 4 次 | 1 次元组调用 | 快 4 倍 |
| 循环边界 | 在循环中调用 array.size(arr) | 在循环前缓存 | 防止卡死 |
| 显示所有输入项 | 界面杂乱 | 使用 active 参数 | 界面简洁 |
| 单一线条样式 | 全部为实线 | 实线/虚线/点线 | 层次更清晰 |
前 3 项优化(与 UDT 相关)效果最大。 优化时要兼顾可读性。不要为了过度优化而牺牲可维护性。
评论
支持 Markdown