Skip to content

语言基础

OpenPine 实现了 Pine Script v6。每个脚本都以版本注解和声明语句开头。

脚本结构

pine
//@version=6
indicator("My Indicator")

// 你的代码
plot(close)

第一行 //@version=6 是必需的。第二行声明脚本类型 — indicator()strategy()library()

注释

pine
// 单行注释
float a = 10  // 行尾注释

//@version=6                          // 编译器注解
//@description A library for math utilities

Pine Script 没有多行注释。以 //@ 开头的特殊注释是编译器注解(version、description 等)。

语句

语句以换行符分隔。不使用分号。多条语句可以用逗号写在同一行:

pine
a = 1
b = 2
c = a + b

// 同一行多条语句
a = 1, b = 2, c = 3

空行和仅包含注释的行会被忽略。

缩进

Pine Script 使用缩进来定义代码块(类似 Python)。一个缩进级别等于 4 个空格1 个 Tab

pine
// 正确:4 个空格
if close > open
    color = color.green
    label.new(bar_index, high, "Up")
else
    color = color.red

// 正确:Tab
if close > open
	doSomething()

// 错误:2 个空格 — 不会被识别为代码块
if close > open
  doSomething()  // 这不是 if 块的一部分!

嵌套块需要累加缩进:

pine
if condition1                    // 级别 0
    if condition2                // 级别 1(4 个空格)
        for i = 0 to 10         // 级别 2(8 个空格)
            doSomething(i)       // 级别 3(12 个空格)

空格和 Tab 可以混用,但每个缩进单位必须是完整的 4 个空格或 1 个 Tab。

续行

长表达式可以跨行。在括号内(圆括号、方括号),允许任意缩进:

pine
plot(
    series = close,
    title = "Close",
    color = color.red,
    linewidth = 2
)

array<int> arr = [
    1,
    2,
    3
]

在括号外,续行必须有额外的空白(比当前块级别至少多 1 个空格或 tab):

pine
// 正确:续行带额外空格
float result = longVariableName
 + anotherLongVariable
 - someOtherValue

// 错误:没有额外空格 — 会被解析为新语句!
float result = a
+ b  // 这是一个独立的语句!

基本类型

原始类型

类型描述示例
int整数。范围:−9,007,199,254,740,992 至 9,007,199,254,740,992(±2⁵³)。42, -123, +5
float浮点数。近似范围:±1.8 × 10³⁰⁸;精度:约 15~17 位有效十进制数字。3.14, .5, 3., 1e-3, 1.2E+5
bool布尔值true, false
string文本"hello", 'world'
colorRGBA 颜色#FF0000, #FF000080
na缺失/未定义值na

intfloat

pine
// 整数
a = 42
b = -123
c = +5

// 浮点数 — 十进制表示
d = 3.14
e = .14        // 前导点
f = 3.         // 尾随点

// 浮点数 — 科学记数法
g = 314e-2     // 3.14
h = 0.00314E+2 // 0.314

string

字符串可以使用单引号或双引号,语义完全相同:

pine
a = "Hello, World!"
b = 'Hello, World!'
full = "Hello, " + "World!"  // 用 + 连接字符串

支持的转义序列:

转义含义
\\反斜杠
\n换行
\r回车
\tTab
\"""双引号(在双引号字符串中)
\'''单引号(在单引号字符串中)

color

颜色字面量使用 # 的十六进制表示法:

  • #RRGGBB — 6 位十六进制,完全不透明(alpha = FF)
  • #RRGGBBAA — 8 位十六进制,显式 alpha 通道
pine
red = #FF0000          // 完全不透明的红色
semiRed = #FF000080    // 半透明红色

命名颜色常量可通过 color 模块获取:color.redcolor.bluecolor.green 等。使用 color.new() 创建自定义透明度的颜色。

na

na 表示缺失或未定义的值。它与任何类型兼容,但单独使用时需要显式类型注解:

pine
float a = na           // OK
int b = na             // OK
a = na                 // 错误:无法推断变量类型

// 使用 na() 检测 na:
if na(myValue)
    // 处理缺失值

运算符

算术运算

pine
a = 10 + 3    // 13
b = 10 - 3    // 7
c = 10 * 3    // 30
d = 10 / 3    // 3.333...
e = 10 % 3    // 1

比较运算

pine
close > open     // 大于
close < open     // 小于
close >= 100     // 大于等于
close <= 100     // 小于等于
close == open    // 等于
close != open    // 不等于

逻辑运算

pine
a and b    // 逻辑与
a or b     // 逻辑或
not a      // 逻辑非

三元运算

pine
color = close > open ? color.green : color.red

历史引用运算符

在 Pine Script 中,每个表达式在每根 K 线上都会求值一次,产生一个时间序列。[] 运算符访问的是某个表达式在前几根 K 线上已经计算好的结果——不仅限于内置变量,而是适用于任何表达式:

pine
// 内置 series 变量
previousClose = close[1]              // 前一根 K 线的收盘价
twoBarsAgo = high[2]                  // 两根 K 线之前的最高价

// 函数调用结果——取回已计算好的值,而非重新求值
prevSma = ta.sma(close, 14)[1]       // 前一根 K 线上计算的 SMA 值
prevEma = ta.ema(close, 10)[3]       // 3 根 K 线之前的 EMA 值

// 算术表达式
prevRange = (high - low)[1]           // 前一根 K 线的振幅

// 用户自定义变量
myValue = close * volume
prevMyValue = myValue[1]              // myValue 在前一根 K 线上的值

核心概念

ta.sma(close, 14)[1]不会重新计算 SMA——它返回的是脚本在前一根 K 线上运行时已经算好的 SMA 值。

在早期 K 线上(历史数据不足时),结果为 na。偏移量可以是 series 表达式:

pine
int lookback = input.int(5, "Lookback")
pastClose = close[lookback]

集合类型

Pine Script 提供三种泛型集合类型:

类型描述
array<T>有序、可索引序列
matrix<T>行 × 列的二维表
map<K, V>键值存储

集合是引用类型——将集合赋值给另一个变量只复制引用,而非内容。使用 .copy() 获取独立副本。

集合通常用 var 声明,这样它们会跨 K 线持久保存,而不是每根 K 线重新创建:

pine
var array<float> highs = array.new<float>()
highs.push(high)   // 每根 K 线累积一个值

即将淘汰的语法

int[]float[] 等简写形式是 array<int>array<float> 的旧式别名,将在未来版本中移除。请始终使用 array<T> 形式。

array<T>

有序的零索引序列,支持动态增删。

创建数组

pine
// 空数组,手动添加元素
var array<float> a = array.new<float>()

// 预分配大小,所有元素初始化为 0.0
array<float> b = array.new<float>(5, 0.0)

// 从字面量列表创建
array<int> c = array.from(10, 20, 30)

读写元素

pine
a.push(close)               // 追加到末尾
a.unshift(close)            // 插入到开头

float last  = a.last()      // 最后一个元素
float first = a.first()     // 第一个元素
float val   = a.get(2)      // 按索引读取(负索引从末尾计数)
a.set(2, 99.0)              // 按索引覆盖

a.insert(1, 42.0)           // 在索引处插入,后续元素右移
float removed = a.remove(1) // 删除指定索引,返回被删元素
float popped  = a.pop()     // 移除并返回最后一个元素
float shifted = a.shift()   // 移除并返回第一个元素

int n = a.size()            // 元素个数

查找

pine
bool found = a.includes(close)    // 值是否存在
int  idx   = a.indexof(close)     // 第一个匹配的索引,或 -1
int  last  = a.lastindexof(close) // 最后一个匹配的索引,或 -1

切片与合并

pine
array<float> sub  = a.slice(1, 4)      // 元素 [1, 4)
array<float> both = a.concat(b)        // 新数组,包含 b 的所有元素
array<float> dup  = a.copy()           // 独立副本
a.reverse()                             // 原地翻转
a.fill(0.0, 0, 3)                       // 将 [0, 3) 范围填充为 0.0
a.clear()                               // 清空所有元素

排序

pine
a.sort()                           // 升序(默认)
a.sort(order.descending)           // 降序
array<int> idx = a.sort_indices()  // 返回排序索引,不修改 a

统计(仅数值数组)

pine
float total = a.sum()
float mean  = a.avg()
float med   = a.median()
float sd    = a.stdev()
float hi    = a.max()
float lo    = a.min()
float rng   = a.range()            // max - min

for...in 遍历

pine
var array<float> vals = array.from(1.0, 2.0, 3.0)
float total = 0.0
for v in vals
    total += v

// 带索引
for [i, v] in vals
    log.info("{0}: {1}", i, v)

map<K, V>

无序的键值存储。键必须是原始类型(intfloatboolstringcolor);值可以是任意类型。

pine
var map<string, float> m = map.new<string, float>()

m.put("open",  open)    // 插入或更新;返回旧值,键不存在时返回 na
m.put("close", close)

float o = m.get("open")       // 键不存在时返回 na
bool  exists = m.contains("close")
int   count  = m.size()

float old = m.remove("open")  // 删除键,返回被删值,键不存在时返回 na

array<string> ks = m.keys()   // 所有键组成的数组
array<float>  vs = m.values() // 所有值组成的数组

m.put_all(other)              // 合并另一个 map
m.clear()                     // 清空所有条目
map<string, float> dup = m.copy()

遍历

pine
for [k, v] in m
    log.info("{0} = {1}", k, v)

matrix<T>

[行, 列] 索引的二维表,均从零开始。

创建

pine
// 3×4 矩阵,所有元素初始化为 0.0
matrix<float> mat = matrix.new<float>(3, 4, 0.0)

读写元素

pine
mat.set(0, 0, 1.5)             // 设置第 0 行第 0 列
float v = mat.get(0, 0)        // 读取元素

int r = mat.rows()             // 行数
int c = mat.columns()          // 列数

array<float> row0 = mat.row(0) // 第 0 行转为数组
array<float> col1 = mat.col(1) // 第 1 列转为数组

结构操作

pine
mat.add_row(na, array.from(1.0, 2.0, 3.0, 4.0)) // 追加一行
mat.add_col()                                     // 追加一列(填充 na)
mat.remove_row()                                  // 删除最后一行(返回该行)
mat.remove_col(0)                                 // 删除第 0 列(返回该列)
mat.reshape(2, 6)                                 // 改变形状(元素总数不变)
matrix<float> sub = mat.submatrix(0, 2, 0, 2)    // 行 [0,2),列 [0,2)
matrix<float> t   = mat.transpose()              // 行列互换
mat.swap_rows(0, 1)
mat.swap_columns(0, 1)
mat.reverse()                                     // 原地翻转元素顺序
mat.fill(0.0)                                     // 填充整个矩阵
mat.sort()                                        // 升序排列所有元素
matrix<float> dup = mat.copy()

数学运算(仅数值矩阵)

pine
float total = mat.avg()
float hi    = mat.max()
float lo    = mat.min()
float med   = mat.median()

matrix<float> s = mat.sum(other)   // 逐元素求和
matrix<float> d = mat.diff(other)  // 逐元素求差
matrix<float> p = mat.multi(other) // 矩阵乘法
matrix<float> i = mat.inv()        // 矩阵求逆
float         det = mat.det()      // 行列式
int           rk  = mat.rank()     // 秩
float         tr  = mat.trace()    // 迹(对角线元素之和)
matrix<float> pw  = mat.pow(2)     // 矩阵幂

元组

元组将函数的多个返回值组合在一起:

pine
calculate(x, y) =>
    [x + y, x - y]          // 返回一个元组

[sum, diff] = calculate(10, 3)

下一步

基于 MIT 许可证发布。