# Pine Coder

> TradingView Pine Script v6 编程专家。编写高质量的指标(indicator)、策略(strategy)和库(library)脚本。 触发条件： - 用户要求编写 Pine Script / PineScript 代码 - 用户提到 TradingView 指标、策略或脚本开发 - 用户需要创建技术分析工具（均线、MACD、RSI 等） - 用户要求编写回测策略或交易系统 - 用户需要帮助调试或优化 Pine Script 代码 - 用户提到 "Pine", "TV脚本", "tradingview 脚本"

- Skill: `discountry/pine-coder` (Agent Skill)
- Install (CLI): `npx skillmds@latest add discountry/pine-coder`
- Raw SKILL.md: https://api.skillmd.com/api/skills/discountry/pine-coder/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: discountry (https://skillmd.com/u/discountry)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/discountry/pine-coder

---


# Pine Script v6 编程专家

编写遵循最新 Pine Script v6 规范的 TradingView 脚本。

## 强制性工作流程

**重要：在编写任何 Pine Script 代码之前，必须遵循以下流程：**

### 1. 文档查阅（必须）

使用 Grep 工具在 `docs/pine-script-docs/` 目录中搜索相关函数、类型或概念的官方定义：

```bash
# 搜索函数签名和用法
Grep pattern="ta\.sma" path="docs/pine-script-docs/"
Grep pattern="strategy\.entry" path="docs/pine-script-docs/"

# 搜索类型定义
Grep pattern="series float" path="docs/pine-script-docs/language/"
Grep pattern="input\.int" path="docs/pine-script-docs/"
```

### 2. 验证清单

编写代码前必须确认：
- [ ] 函数名称拼写正确（如 `ta.sma` 而非 `sma`）
- [ ] 参数类型匹配（如 `length` 需要 `simple int`）
- [ ] 返回类型正确（如 `ta.sma()` 返回 `series float`）
- [ ] 使用的是 v6 语法（非废弃的 v4/v5 语法）

### 3. 常见错误预防

在编写代码时主动检查以下易错点：
- 布尔值不能为 `na`（v6 特性）
- `and`/`or` 短路求值可能跳过函数调用
- 整数除法返回浮点数（`5/2 = 2.5`）
- `transp` 参数已移除，使用 `color.new()`
- `when` 参数已移除，使用 `if` 条件

---

## 核心命名空间参考

### 技术分析 `ta.*`

| 函数 | 签名 | 返回类型 |
|------|------|----------|
| `ta.sma()` | `ta.sma(source, length)` | series float |
| `ta.ema()` | `ta.ema(source, length)` | series float |
| `ta.rsi()` | `ta.rsi(source, length)` | series float |
| `ta.macd()` | `ta.macd(source, fastlen, slowlen, siglen)` | [series float, series float, series float] |
| `ta.crossover()` | `ta.crossover(source1, source2)` | series bool |
| `ta.crossunder()` | `ta.crossunder(source1, source2)` | series bool |
| `ta.highest()` | `ta.highest(source, length)` | series float |
| `ta.lowest()` | `ta.lowest(source, length)` | series float |
| `ta.atr()` | `ta.atr(length)` | series float |
| `ta.stoch()` | `ta.stoch(source, high, low, length)` | series float |
| `ta.bb()` | `ta.bb(series, length, mult)` | [series float, series float, series float] |
| `ta.supertrend()` | `ta.supertrend(factor, atrPeriod)` | [series float, series float] |

**注意：** `length` 参数通常需要 `simple int` 类型，不接受 `series int`。

### 策略 `strategy.*`

| 函数 | 用途 | 关键参数 |
|------|------|----------|
| `strategy.entry()` | 入场订单 | `id`, `direction`, `qty`, `limit`, `stop` |
| `strategy.exit()` | 出场订单 | `id`, `from_entry`, `profit`, `loss`, `limit`, `stop`, `trail_points`, `trail_offset` |
| `strategy.close()` | 关闭指定入场 | `id` |
| `strategy.close_all()` | 关闭所有持仓 | `comment` |
| `strategy.order()` | 基础订单 | `id`, `direction`, `qty`, `limit`, `stop` |
| `strategy.cancel()` | 取消订单 | `id` |
| `strategy.cancel_all()` | 取消所有订单 | - |

**方向常量：**
- `strategy.long` - 做多
- `strategy.short` - 做空

**订单类型：**
- 市价单：不指定 `limit` 和 `stop`
- 限价单：指定 `limit`
- 止损单：指定 `stop`
- 止损限价单：同时指定 `limit` 和 `stop`

### 输入 `input.*`

| 函数 | 返回类型 | 示例 |
|------|----------|------|
| `input.int()` | input int | `input.int(14, "Length", minval = 1)` |
| `input.float()` | input float | `input.float(1.5, "Multiplier", step = 0.1)` |
| `input.bool()` | input bool | `input.bool(true, "Show Signal")` |
| `input.string()` | input string | `input.string("SMA", "MA Type", options = ["SMA", "EMA"])` |
| `input.color()` | input color | `input.color(color.blue, "Line Color")` |
| `input.source()` | series float | `input.source(close, "Source")` |
| `input.timeframe()` | input string | `input.timeframe("D", "Timeframe")` |
| `input.symbol()` | input string | `input.symbol("AAPL", "Symbol")` |
| `input.session()` | input string | `input.session("0930-1600", "Session")` |
| `input.enum()` | enum type | `input.enum(maType.SMA, "MA Type")` |

### 数学 `math.*`

| 函数 | 说明 |
|------|------|
| `math.abs()` | 绝对值 |
| `math.max()` | 最大值 |
| `math.min()` | 最小值 |
| `math.round()` | 四舍五入 |
| `math.floor()` | 向下取整 |
| `math.ceil()` | 向上取整 |
| `math.pow()` | 幂运算 |
| `math.sqrt()` | 平方根 |
| `math.log()` | 自然对数 |
| `math.log10()` | 以10为底对数 |
| `math.avg()` | 平均值 |
| `math.sum()` | 求和 |
| `math.round_to_mintick()` | 按最小价格变动单位四舍五入 |

### 字符串 `str.*`

| 函数 | 说明 |
|------|------|
| `str.tostring()` | 转换为字符串 |
| `str.tonumber()` | 转换为数字 |
| `str.format()` | 格式化字符串 |
| `str.length()` | 字符串长度 |
| `str.contains()` | 包含检查 |
| `str.replace()` | 替换 |
| `str.split()` | 分割 |
| `str.upper()` | 转大写 |
| `str.lower()` | 转小写 |

### 颜色 `color.*`

| 函数/常量 | 说明 |
|-----------|------|
| `color.new()` | 创建带透明度的颜色 |
| `color.rgb()` | 从 RGB 值创建颜色 |
| `color.from_gradient()` | 渐变色 |
| `color.r()` | 获取红色分量 |
| `color.g()` | 获取绿色分量 |
| `color.b()` | 获取蓝色分量 |
| `color.t()` | 获取透明度 |

**颜色常量：** `color.red`, `color.green`, `color.blue`, `color.yellow`, `color.orange`, `color.purple`, `color.white`, `color.black`, `color.gray`, `color.aqua`, `color.fuchsia`, `color.lime`, `color.maroon`, `color.navy`, `color.olive`, `color.silver`, `color.teal`

### 请求 `request.*`

| 函数 | 说明 | 关键参数 |
|------|------|----------|
| `request.security()` | 请求其他品种/周期数据 | `symbol`, `timeframe`, `expression` |
| `request.security_lower_tf()` | 请求更低周期数据 | `symbol`, `timeframe`, `expression` |
| `request.currency_rate()` | 货币汇率 | `from`, `to` |
| `request.dividends()` | 股息数据 | `ticker`, `field` |
| `request.earnings()` | 收益数据 | `ticker`, `field` |
| `request.financial()` | 财务数据 | `symbol`, `financial_id`, `period` |

### 数组 `array.*`

| 函数 | 说明 |
|------|------|
| `array.new<type>()` | 创建数组 |
| `array.push()` | 添加元素到末尾 |
| `array.pop()` | 移除并返回末尾元素 |
| `array.get()` | 获取元素 |
| `array.set()` | 设置元素 |
| `array.size()` | 数组大小 |
| `array.avg()` | 平均值 |
| `array.sum()` | 求和 |
| `array.max()` | 最大值 |
| `array.min()` | 最小值 |
| `array.sort()` | 排序 |
| `array.slice()` | 切片 |
| `array.from()` | 从值创建数组 |

---

## 类型系统

### 类型限定符 (由弱到强)

| 限定符 | 说明 | 何时确定 | 示例 |
|--------|------|----------|------|
| `const` | 编译时常量 | 编译时 | `const int MAX = 100` |
| `input` | 输入值，运行时不变 | 输入时 | `input.int()` 返回值 |
| `simple` | 首根K线确定后不变 | 第一根K线 | `syminfo.ticker` |
| `series` | 每根K线可变 | 每根K线 | `close`, `high`, `low` |

**重要规则：** 弱类型可以传给强类型参数，反之不行。例如 `const int` 可以传给 `simple int` 参数，但 `series int` 不能传给 `simple int` 参数。

### 基础类型

| 类型 | 说明 | 字面量示例 |
|------|------|------------|
| `int` | 整数 | `1`, `-42`, `0` |
| `float` | 浮点数 | `1.5`, `3.14`, `1e-5` |
| `bool` | 布尔值 | `true`, `false` |
| `color` | 颜色 | `#FF0000`, `color.red` |
| `string` | 字符串 | `"text"`, `'text'` |

### 引用类型

`line`, `linefill`, `box`, `polyline`, `label`, `table`, `chart.point`, `array`, `matrix`, `map`

**注意：** 引用类型始终为 `series` 限定符。

---

## 脚本类型

| 类型 | 声明 | 必须包含 |
|------|------|----------|
| 指标 | `indicator()` | 至少一个输出函数 (plot, plotshape, barcolor, line.new 等) |
| 策略 | `strategy()` | 至少一个订单命令 (strategy.entry, strategy.order 等) |
| 库 | `library()` | 至少一个 `export` 的函数/方法/类型/枚举 |

---

## 标准脚本结构

```pinescript
// 1. 许可证注释 (可选)
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0

// 2. 版本注解
//@version=6

// 3. 声明语句
indicator("Script Name", overlay = true)

// 4. 导入语句
import library/name/1 as lib

// 5. 常量声明 (SNAKE_CASE)
const int MAX_BARS = 500

// 6. 输入参数
int lengthInput = input.int(14, "Length", minval = 1)

// 7. 函数定义
//@function Calculate moving average
//@param src Source series
//@param len Length
//@returns Moving average value
ma(float src, int len) =>
    ta.sma(src, len)

// 8. 计算逻辑
float maValue = ma(close, lengthInput)

// 9. 策略调用 (仅策略)
// strategy.entry(...)

// 10. 可视化
plot(maValue, "MA", color.blue)

// 11. 警报
alertcondition(ta.crossover(close, maValue), "Price Cross MA")
```

---

## v6 关键变化

| 变化 | v5 语法 | v6 语法 |
|------|---------|---------|
| 动态请求 | 需要 `dynamic_requests = true` | 默认启用 |
| 布尔值 na | `na(boolVar)` 有效 | 布尔值不能为 na |
| and/or | 总是执行两边 | 短路求值 |
| 整数除法 | `5/2 = 2` | `5/2 = 2.5` |
| timeframe.period | `"D"` | `"1D"` |
| 数组负索引 | 不支持 | `arr[-1]` = 最后元素 |
| transp 参数 | `plot(..., transp = 50)` | `color.new(color, 50)` |
| when 参数 | `strategy.entry(..., when = cond)` | `if cond` 包裹 |

---

## 命名规范

```pinescript
// 变量和函数: camelCase
float fastMa = ta.sma(close, 10)

// 常量: SNAKE_CASE
const color BULL_COLOR = color.green

// 输入变量后缀 Input
int lengthInput = input.int(14, "Length")

// 明确类型声明
float ma = ta.sma(close, 14)  // 推荐
ma = ta.sma(close, 14)        // 可以但不推荐
```

---

## 文档注解

```pinescript
//@variable 描述变量
float price = close

//@function 函数描述
//@param paramName 参数描述
//@returns 返回值描述
export myFunction(float paramName) =>
    paramName * 2
```

---

## 常见限制

| 限制项 | 数值 |
|--------|------|
| 最大 plot 数量 | 64 |
| 最大 labels/lines/boxes | 500 (可通过 max_*_count 配置) |
| 最大 request.*() 调用 | 40-64 (取决于账户) |
| 集合最大元素 | 100,000 |
| 历史缓冲区 | 5000 bars (大多数系列), 10000 (OHLC, time) |
| 编译超时 | 2 分钟 |
| 执行超时 | 20-40 秒 |

---

## 调试技巧

```pinescript
// Pine Logs 面板
log.info("Value: {0}", variable)
log.warning("Warning message")
log.error("Error: {0}", errorInfo)

// 图表标签调试
if condition
    label.new(bar_index, high, str.tostring(value), tooltip = "详细信息")

// 数值可视化
plot(debugValue, "Debug", display = display.data_window)

// 条件可视化
bgcolor(condition ? color.new(color.green, 90) : na)
```

---

## 策略开发要点

### 策略声明

```pinescript
strategy("My Strategy",
    overlay = true,
    pyramiding = 3,           // 同向最大加仓次数
    default_qty_type = strategy.percent_of_equity,
    default_qty_value = 10,   // 每次交易10%资金
    initial_capital = 10000,
    commission_type = strategy.commission.percent,
    commission_value = 0.1,
    slippage = 2,
    margin_long = 100,        // v6 默认100%
    margin_short = 100
)
```

### 订单类型

- **市价单**: 默认，下一tick执行
- **限价单**: `limit` 参数指定价格
- **止损单**: `stop` 参数指定价格
- **止损限价单**: 同时指定 `stop` 和 `limit`

---

## 性能优化

1. **避免循环内调用 request.*()**（除非必要）
2. **使用 var 减少重复初始化**
   ```pinescript
   var float[] prices = array.new<float>()
   ```
3. **条件外预计算依赖历史的函数**
   ```pinescript
   float rsiValue = ta.rsi(close, 14)  // 全局作用域
   if someCondition and rsiValue > 70
       // ...
   ```
4. **使用 max_bars_back 避免历史缓冲区错误**
5. **减少 plot 数量**，使用 `display = display.none` 隐藏不必要的图表显示

---

## 重绘问题

重绘发生在脚本在实时K线上重新计算时。避免方法：

```pinescript
// 使用确认K线
if barstate.isconfirmed
    // 仅在K线确认后执行逻辑

// 避免 lookahead
request.security(symbol, tf, expression, lookahead = barmerge.lookahead_off)

// 使用历史值
close[1]  // 前一根确认K线的收盘价
```

---

## 示例：完整策略模板

```pinescript
//@version=6
strategy("MA Cross Strategy",
    overlay = true,
    pyramiding = 0,
    default_qty_type = strategy.percent_of_equity,
    default_qty_value = 100)

// 输入
int fastLength = input.int(10, "Fast MA", minval = 1)
int slowLength = input.int(20, "Slow MA", minval = 1)
float takeProfitPct = input.float(2.0, "Take Profit %", step = 0.1)
float stopLossPct = input.float(1.0, "Stop Loss %", step = 0.1)

// 计算
float fastMa = ta.sma(close, fastLength)
float slowMa = ta.sma(close, slowLength)

// 条件
bool longCondition = ta.crossover(fastMa, slowMa)
bool shortCondition = ta.crossunder(fastMa, slowMa)

// 策略逻辑
if longCondition
    strategy.entry("Long", strategy.long)
    strategy.exit("Long Exit", "Long",
        profit = close * takeProfitPct / 100 / syminfo.mintick,
        loss = close * stopLossPct / 100 / syminfo.mintick)

if shortCondition
    strategy.entry("Short", strategy.short)
    strategy.exit("Short Exit", "Short",
        profit = close * stopLossPct / 100 / syminfo.mintick,
        loss = close * takeProfitPct / 100 / syminfo.mintick)

// 可视化
plot(fastMa, "Fast MA", color.green)
plot(slowMa, "Slow MA", color.red)
plotshape(longCondition, "Buy", shape.triangleup, location.belowbar, color.green)
plotshape(shortCondition, "Sell", shape.triangledown, location.abovebar, color.red)
```

---

## 文档参考

项目目录 `docs/pine-script-docs/` 包含完整的 Pine Script v6 官方文档：

- `primer/` - 入门教程
- `language/` - 语言核心概念（执行模型、类型系统、运算符等）
- `visuals/` - 可视化相关（plots、drawings、colors、tables）
- `concepts/` - 策略、警报、时间周期、库等
- `writing/` - 代码风格、调试、优化、限制
- `faq/` - 常见问题（按主题分类）
- `migration-guides/` - 版本迁移指南

**遇到不确定的函数、类型或用法时，必须先使用 Grep 搜索相关文档：**

```bash
# 搜索示例
Grep pattern="function_name" path="docs/pine-script-docs/"
Grep pattern="参数名" path="docs/pine-script-docs/"
```

