关于 pine-developer
pine-developer 是面向 TradingView Pine Script v6 开发的技能,提供代码模式,并涵盖语言更新、脚本架构、平台限制和代码审查。
主要功能
- 01Pine Script v6 更新指南
- 02指标与策略模板
- 03优先使用 UDT 的架构模式
- 04避免重绘的建议
- 05代码审查清单
适用场景
- 01编写 TradingView 指标
- 02实现 TradingView 策略
- 03检查 Pine Script 是否符合平台限制
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ traderspost/ pinescript-agents --skill pine-developer安装后在 Claude Code 中输入/pine-developer确认已加载。
详细介绍
Pine Script 开发者
专注于为 TradingView 编写可用于生产环境的高质量 Pine Script v6 代码。
⚠️ 关键:Pine Script v6 2025 年更新
这些更新对于编写正确、现代的 Pine Script 代码至关重要:
2025 年 12 月:更新换行规则
括号内的表达式现在支持灵活缩进:
// ✅ NOW VALID - Any indentation inside parentheses
plot(series,
title="My Plot", // Can use any indentation
color=color.blue)
// ✅ Also valid
longCondition = (ta.crossover(ema50, ema200) and
rsi < 30 and
volume > ta.sma(volume, 20))
// ⚠️ STILL REQUIRED outside parentheses - non-multiple-of-4 indentation
longCondition = ta.crossover(ema50, ema200) and
rsi < 30 // Must NOT be multiple of 4 spaces
2025 年 3 月:重大变更 - for 循环动态边界
for 循环现在会在每次迭代前计算 to_num,而非仅计算一次。
首选:对集合使用 for...in 循环:
// ✅ BEST: Use for...in for arrays (safe, clean, preferred)
for element in myArray
// Process element directly
// ✅ BEST: Use for...in with index when needed
for [index, element] in myArray
// Have both index and value
// ✅ for...in works with arrays, matrices, and maps
for [key, value] in myMap
// Iterate key-value pairs
如果必须使用传统的 for 循环:
// ⚠️ BREAKING: This creates infinite loop!
var arr = array.new<int>()
for i = 0 to array.size(arr) // Re-evaluated each iteration
array.push(arr, i) // Array grows, loop never ends!
// ✅ FALLBACK: Cache the boundary if using traditional for
size = array.size(arr)
for i = 0 to size
array.push(arr, i)
注意: for...in 允许在迭代期间修改数组/矩阵的大小。映射不能在 for...in 期间调整大小——请改用 map.keys() 数组。
2025 年 2 月:移除作用域数量限制
- 之前总共限制为 550 个作用域
- 现在函数、方法、循环、条件语句、类型和枚举中的作用域数量不受限制
2025 年 2 月:买卖价变量
// Only available on 1T (tick) timeframe
bidPrice = bid // Highest buyer price (na on other timeframes)
askPrice = ask // Lowest seller price (na on other timeframes)
Pine Script v6 2025 年新功能
2025 年 7 月:输入函数的 active 参数
现在所有输入函数都支持条件激活:
useCustomMA = input.bool(false, "Use Custom MA")
customMALength = input.int(20, "Custom MA Length",
active=useCustomMA, // Grayed out when useCustomMA is false
tooltip="Only available when 'Use Custom MA' is enabled")
2025 年 7 月:syminfo.current_contract
// For continuous futures - returns underlying contract ticker
currentContract = syminfo.current_contract // Returns na for non-continuous
2025 年 11 月:syminfo.isin
// International Securities Identification Number (12 chars)
isinCode = syminfo.isin // Empty string if unavailable
2025 年 9 月:绘图线条样式
plot(series, linestyle=plot.linestyle_solid) // Default
plot(series, linestyle=plot.linestyle_dashed) // Dashed line
plot(series, linestyle=plot.linestyle_dotted) // Dotted line
2025 年 10 月:增强的 time() 和 time_close()
新增 timeframe_bars_back 参数:
// Get time from 5 bars back on the DAILY timeframe (not current chart)
dailyTimeBack = time("D", timeframe_bars_back=5)
// Positive = past bars, Negative = future (expected) timestamps
futureTime = time("D", timeframe_bars_back=-1) // Expected next bar
2025 年 8 月:增加字符串长度
- 字符串最大长度:40,960 个字符(原为 4,096)
2025 年 6 月:库常量导出
// In library:
//@version=6
library("MyLib")
export const float PI = 3.14159265359
export const int MAX_BARS = 500
export const string VERSION = "1.0.0"
2025 年 5 月:非基于时间的图表中的 time_close
适用于 Renko、Line Break、Kagi、Point & Figure、Range 图表:
- 开盘实时柱的
time_close现在会返回na - 柱线收盘后,
time_close[1]现在可以立即使用
2025 年 4 月:基于百分比的代码样式
// New "PercentageLTP" style for percentage box sizing
renkoTicker = ticker.renko(syminfo.tickerid, "PercentageLTP", 1.0)
kagiTicker = ticker.kagi(syminfo.tickerid, "PercentageLTP", 0.5)
pnfTicker = ticker.pointfigure(syminfo.tickerid, "PercentageLTP", 1.0)
2025 年 3 月:box.set_xloc()
// Modify box x-coordinates after creation
box.set_xloc(myBox, left, right, xloc.bar_index)
box.set_xloc(myBox, leftTime, rightTime, xloc.bar_time)
关键:换行规则(2025 年 12 月更新)
括号内(灵活)
// ✅ Any indentation works inside ()
plot(series,
title="Plot",
color=color.blue, // Different indentation OK
linewidth=2)
indicator("Title",
overlay=true,
max_bars_back=500)
括号外(严格)
// ✅ CORRECT - Non-multiple-of-4 indentation
longCondition = ta.crossover(ema50, ema200) and
rsi < 30 and
volume > ta.sma(volume, 20)
// ❌ WRONG - Multiple of 4 (or same level)
longCondition = ta.crossover(ema50, ema200) and
rsi < 30 // Error: 4 spaces
longCondition = ta.crossover(ema50, ema200) and
rsi < 30 // Error: same indentation
关键:三元运算符——保持在同一行
// ❌ NEVER split ternary across lines
text = condition ?
"value1" :
"value2" // ERROR!
// ✅ ALWAYS keep on one line
text = condition ? "value1" : "value2"
// ✅ For long ternaries, use intermediate variables
trueText = str.format("Long value {0}", param1)
falseText = str.format("Other value {0}", param2)
result = condition ? trueText : falseText
关键:绘图作用域限制
绝不要在局部作用域内使用 plot():
// ❌ WRONG - All of these fail:
if condition
plot(value) // ERROR: Cannot use 'plot' in local scope
for i = 0 to 10
plot(close[i]) // ERROR!
myFunc() =>
plot(close) // ERROR!
// ✅ CORRECT patterns:
plot(condition ? value : na) // Conditional plotting
plot(value, color=condition ? color.blue : na) // Conditional color
// For dynamic drawing in local scopes, use:
if condition
line.new(...) // OK
label.new(...) // OK
box.new(...) // OK
⚠️ 关键:UDT 优先架构
对于任何复杂数据结构,务必先定义用户自定义类型(UDT),再编写其他代码。
为什么要 UDT 优先?
- 封装:将相关数据和绘图对象打包在一起
- 方法:使用
method语法直接为类型添加行为 - 无限回溯:使用
xloc.bar_time而不是xloc.bar_index(后者限制为 5000 根柱线!) - 整洁数组:使用一个
array<MyType>,而不是 6 个以上的并行数组 - 自动管理绘图:类型可以创建/删除自己的可视化对象
UDT 定义模式
// ALWAYS define UDTs early in script (after inputs, before calculations)
type Regression
// Time coordinates (for xloc.bar_time - NO lookback limit!)
int startTime
int endTime
// Bar indices (for Y calculations: y = slope * barIndex + intercept)
int startBarIndex
int endBarIndex
// Data parameters
float slope
float intercept
float stdDev
float pearsonR
// State flags
bool isLive = false
bool isOriginal = false
// Drawing objects (managed internally)
line centerLine = na
line upperLine = na
line lowerLine = na
linefill fill = na
UDT 方法模式
// Delete method - clean up drawing objects
method delete(Regression this) =>
if not na(this.centerLine)
line.delete(this.centerLine)
if not na(this.upperLine)
line.delete(this.upperLine)
if not na(this.lowerLine)
line.delete(this.lowerLine)
if not na(this.fill)
linefill.delete(this.fill)
this.centerLine := na
this.upperLine := na
this.lowerLine := na
this.fill := na
// Calculation method
method calculateY(Regression this, int barIdx) =>
this.slope * barIdx + this.intercept
// Draw method - use xloc.bar_time for unlimited lookback!
method draw(Regression this, color baseColor, int width) =>
this.delete() // Clean up old drawings first
startY = this.calculateY(this.startBarIndex)
endY = this.calculateY(this.endBarIndex)
// ✅ CRITICAL: Use xloc.bar_time - NO 5000 bar limit!
this.centerLine := line.new(
this.startTime, startY,
this.endTime, endY,
xloc=xloc.bar_time, // ← UNLIMITED LOOKBACK
color=baseColor,
width=width)
⚠️ 关键:xloc.bar_time 与 xloc.bar_index
对于可能需要显示历史数据的绘图对象,务必使用 xloc.bar_time:
// ❌ WRONG - Limited to 5000 bars back
line.new(bar_index - 100, y1, bar_index, y2, xloc=xloc.bar_index)
// ✅ CORRECT - Unlimited historical lookback
line.new(time[100], y1, time, y2, xloc=xloc.bar_time)
// Store TIME when events occur, not just bar_index
var int eventTime = na
var int eventBarIndex = na
if eventOccurred
eventTime := time // For drawing
eventBarIndex := bar_index // For Y calculation
UDT 存储模式
// Single array of UDTs instead of parallel arrays
var array<Regression> regressions = array.new<Regression>()
var Regression liveRegression = na
var Regression originalRegression = na
// ❌ WRONG - Parallel arrays (hard to maintain)
var array<float> slopes = array.new<float>()
var array<float> intercepts = array.new<float>()
var array<int> startBars = array.new<int>()
var array<int> endBars = array.new<int>()
// ... 6+ arrays to keep in sync!
// ✅ CORRECT - Single UDT array
var array<Regression> regressions = array.new<Regression>()
创建和存储 UDT
// Create new instance with all data
newRegression = Regression.new(
startTime = capturedStartTime,
endTime = capturedEndTime,
startBarIndex = capturedStartBar,
endBarIndex = capturedEndBar,
slope = calculatedSlope,
intercept = calculatedIntercept,
stdDev = calculatedStdDev,
pearsonR = calculatedR
)
// Add to collection
array.push(regressions, newRegression)
使用 for...in 遍历 UDT 数组
// ✅ BEST: Use for...in for UDT arrays
for reg in regressions
baseColor = reg.slope >= 0 ? uptrendColor : downtrendColor
reg.draw(baseColor, lineWidth)
// ✅ With index when needed
for [i, reg] in regressions
if i == array.size(regressions) - 1
// Special handling for last item
时间捕获模式
// Capture BOTH time and bar_index when events occur
var int startTime = na
var int startBarIndex = na
var int endTime = na
var int endBarIndex = na
if eventStart
startTime := time // For xloc.bar_time drawing
startBarIndex := bar_index // For Y calculations
if eventEnd
endTime := time
endBarIndex := bar_index
// Create UDT with both coordinate systems
newItem = MyType.new(
startTime = startTime,
endTime = endTime,
startBarIndex = startBarIndex,
endBarIndex = endBarIndex
)
UDT 架构检查清单
- 在任何计算之前定义 UDT 类型
- 存储绘图坐标所需的
time值 - 存储用于 Y 轴计算的
bar_index值 - 将绘图对象(
line、box、label)作为 UDT 字段 - 添加
delete()方法以清理绘图 - 添加使用
xloc.bar_time的draw()方法 - 使用单个
array<MyType>,而不是并行数组 - 使用
for...in遍历 UDT 数组
文档访问
主要文档参考:
/docs/pinescript-v6/quick-reference/syntax-basics.md- 核心语法/docs/pinescript-v6/reference-tables/function-index.md- 函数参考/docs/pinescript-v6/core-concepts/execution-model.md- 执行模型/docs/pinescript-v6/core-concepts/repainting.md- 重绘问题/docs/pinescript-v6/quick-reference/limitations.md- 平台限制
项目文件管理
- 将工作保存到
/projects/[project-name].pine - 使用准确的项目信息更新文件头部
- 切勿创建不必要的文件
脚本结构模板
指标模板
// built with PineScript Agents by TradersPost
//@version=6
indicator(title="", shorttitle="", overlay=true)
策略模板(重要:包含警报注解)
// built with PineScript Agents by TradersPost
//@version=6
strategy(title="", shorttitle="", overlay=true)
//@strategy_alert_message {{strategy.order.alert_message}}
// ============================================================================
// INPUTS
// ============================================================================
// Group 1: Main Settings
setting1 = input.int(14, "Setting", group="Main Settings",
tooltip="Description of what this does")
// Group 2: Visual Settings
showPlots = input.bool(true, "Show Plots", group="Visual Settings")
plotColor = input.color(color.blue, "Plot Color", group="Visual Settings",
active=showPlots) // Grayed out when showPlots is false
// ============================================================================
// CALCULATIONS
// ============================================================================
// Cache boundaries for loops (March 2025 breaking change)
arraySize = array.size(myArray)
for i = 0 to arraySize - 1
// Process...
// ============================================================================
// CONDITIONS
// ============================================================================
// ============================================================================
// PLOTS
// ============================================================================
// Use new linestyle parameter (September 2025)
plot(ma, "MA", color.blue, linewidth=2, linestyle=plot.linestyle_solid)
// ============================================================================
// ALERTS
// ============================================================================
TradingView 限制(2025 年更新)
限制
- 历史引用最多 500 根 K 线
- plot/hline/fill 输出最多 500 个
- 绘图对象(label/line/box/table)最多 64 个
- security() 调用最多 40 次
- 编译后的脚本大小最多 100KB
- 表格:最多 100 个单元格
- 数组:最多 100,000 个元素
- 字符串:最多 40,960 个字符(2025 年 8 月上限提高)
- 作用域:无限制(2025 年 2 月取消限制)
平台特性
- bar_index 从 0 开始
- 计算中的 na 传播
- 历史计算与实时计算之间存在差异
- 策略在 K 线收盘时计算(除非启用 calc_on_every_tick)
bid/ask仅在 1T 时间周期可用
最佳实践
避免重绘
// Use barstate.isconfirmed for signals
if barstate.isconfirmed and buyCondition
strategy.entry("Long", strategy.long)
// Proper request.security() usage
htfClose = request.security(syminfo.tickerid, "D", close,
lookahead=barmerge.lookahead_off)
性能优化
// Cache repeated calculations
sma20 = ta.sma(close, 20)
sma50 = ta.sma(close, 50)
crossUp = ta.crossover(sma20, sma50)
// Combine security() calls
[htfClose, htfHigh, htfLow] = request.security(syminfo.tickerid, "D",
[close, high, low])
用户体验(使用 2025 年功能)
// Input groups with active parameter
advancedMode = input.bool(false, "Advanced Mode", group="Settings")
advSetting = input.int(10, "Advanced Setting",
group="Settings",
active=advancedMode, // July 2025 feature
tooltip="Only visible in advanced mode")
错误处理
// Check for na values
safeValue = na(value) ? 0 : value
// Handle division by zero
safeDiv = denominator != 0 ? numerator / denominator : 0
// Cache loop boundaries (March 2025 breaking change)
arrSize = array.size(arr)
for i = 0 to arrSize - 1
// ...
代码审查清单
架构(优先检查!)
- 复杂数据采用 UDT 优先模式
- 绘图对象使用
xloc.bar_time(而非 bar_index) - 捕获时间值以用于历史绘图
- 使用单个 UDT 数组,而非并行数组
脚本基础
-
//@version=6声明 - 策略使用
//@strategy_alert_message {{strategy.order.alert_message}}(放置在strategy()调用之后) - 标题和 overlay 设置正确
输入项与用户体验
- 输入项包含工具提示和分组
- 在适当情况下使用输入项的
active参数 - 在适当情况下使用新的绘图线型
- 绘图样式正确
代码质量
- 不存在重绘问题
- 正确处理 na 值
- 缓存循环边界(2025 年 3 月修复)
- 计算高效
- 变量名称清晰
- 根据需要设置警报条件
编写可用于生产环境的高效代码,并采用 UDT 优先架构以及最新的 Pine Script v6 功能。
评论
支持 Markdown