对象
介绍
Pine Script™ 对象是用户定义类型(UDT) 的实例。它们相当于包含称为字段的部分的变量,每个字段都可以保存各种类型的独立值。
经验丰富的程序员可以将 UDT 视为无方法类。它们允许用户创建自定义类型,将不同的值组织在一个逻辑实体下。
创建对象
在创建对象之前,必须先定义其类型。 类型系统页面的 用户定义类型部分介绍了如何执行此操作。
让我们定义一个pivotPoint
类型来保存枢轴信息:
type pivotPoint
int x
float y
string xloc = xloc.bar_time
注意:
- 我们使用 type 关键字来声明 UDT 的创建。
- 我们将我们的新作品命名为 UDT
pivotPoint
。 - 在第一行之后,我们创建一个包含每个字段的类型和名称的本地块。
- 该
x
字段将保存枢轴的 x 坐标。它被声明为“int”,因为它将保存“int”类型的时间戳或条形索引。 y
是一个“浮动”,因为它将保存枢轴点的价格。xloc
是一个字段,它将指定xloc.bar_index 或 xloc.bar_timex
的 单位。我们 使用运算符将其默认值设置为 xloc.bar_time。当从该 UDT 创建对象时,其字段将设置为该值。=
xloc
现在我们的pivotPoint
UDT 已经定义好了,我们可以继续从中创建对象。我们使用 UDT 的new()
内置方法创建对象。foundPoint
要从我们的pivotPoint
UDT 创建新对象,我们使用:
foundPoint = pivotPoint.new()
我们还可以使用以下命令为创建的对象指定字段值:
foundPoint = pivotPoint.new(time, high)
或者等效的:
foundPoint = pivotPoint.new(x = time, y = high)
此时,foundPoint
对象的字段将包含
创建时内置的timex
的值
,将包含high的值
,并且字段将包含其默认值
xloc.bar_time
,因为在创建对象时没有为其定义任何值。y
xloc
还可以通过以下方式声明na对象名称来创建对象占位符 :
pivotPoint foundPoint = na
此示例显示检测到高枢轴的标签。枢轴是legsInput
在出现后检测到的条形图,因此我们必须在过去绘制标签,以使其出现在枢轴上:
//@version=5
indicator("Pivot labels", overlay = true)
int legsInput = input(10)
// Define the `pivotPoint` UDT.
type pivotPoint
int x
float y
string xloc = xloc.bar_time
// Detect high pivots.
pivotHighPrice = ta.pivothigh(legsInput, legsInput)
if not na(pivotHighPrice)
// A new high pivot was found; display a label where it occurred `legsInput` bars back.
foundPoint = pivotPoint.new(time[legsInput], pivotHighPrice)
label.new(
foundPoint.x,
foundPoint.y,
str.tostring(foundPoint.y, format.mintick),
foundPoint.xloc,
textcolor = color.white)
请注意上面例子中的这一行:
foundPoint = pivotPoint.new(time[legsInput], pivotHighPrice)
也可以使用下面的方式编写:
pivotPoint foundPoint = na
foundPoint := pivotPoint.new(time[legsInput], pivotHighPrice)
当使用 var关键字声明分配给用户定义类型 的对象的变量时 ,该关键字会自动应用于该对象的所有字段:
//@version=5
indicator("Objects using `var` demo")
//@type A custom type to hold index, price, and volume information.
type BarInfo
int index = bar_index
float price = close
float vol = volume
//@variable A `BarInfo` instance whose fields persist through all iterations, starting from the first bar.
var BarInfo firstBar = BarInfo.new()
//@variable A `BarInfo` instance declared on every bar.
BarInfo currentBar = BarInfo.new()
// Plot the `index` fields of both instances to compare the difference.
plot(firstBar.index)
plot(currentBar.index)
需要注意的是,将对象分配给使用 varip 关键字的变量不会自动允许对象的字段在每次内部更新时不回滚的情况下持续存在。必须将关键字应用于类型声明中的每个所需字段才能实现此行为。例如:
//@version=5
indicator("Objects using `varip` fields demo")
//@type A custom type that counts the bars and ticks in the script's execution.
type Counter
int bars = 0
varip int ticks = 0
//@variable A `Counter` object whose reference persists throughout all bars.
var Counter counter = Counter.new()
// Add 1 to the `bars` and `ticks` fields. The `ticks` field is not subject to rollback on unconfirmed bars.
counter.bars += 1
counter.ticks += 1
// Plot both fields for comparison.
plot(counter.bars, "Bar counter", color.blue, 3)
plot(counter.ticks, "Tick counter", color.purple, 3)
注意:
- 我们使用
var
关键字来指定
Counter
分配给counter
变量的对象在整个脚本执行过程中持续存在。 - 该
bars
字段在实时条上回滚,而 由于我们 在其声明中包含了varipticks
,因此该字段不会回滚 。
更改字段值
可以使用:=重新赋值运算符来更改对象字段的值 。
我们前面例子中的这一行:
foundPoint = pivotPoint.new(time[legsInput], pivotHighPrice)
可以使用以下命令编写:
foundPoint = pivotPoint.new()
foundPoint.x := time[legsInput]
foundPoint.y := pivotHighPrice
收集物品
Pine Script™ 集合(数组和映射)可以包含对象,允许用户向其数据结构添加虚拟维度。要声明对象集合,请将 UDT 名称传递到其 类型模板中。
此示例声明一个空
数组
,用于保存pivotPoint
用户定义类型的对象:
pivotHighArray = array.new<pivotPoint>()
要明确将变量的类型声明为 数组、 矩阵或用户定义类型 的 映射,请使用集合的类型关键字,后跟其 类型模板。例如:
var array<pivotPoint> pivotHighArray = na
pivotHighArray := array.new<pivotPoint>()
让我们利用所学知识创建一个检测高枢轴点的脚本。该脚本首先将历史枢轴点信息收集到一个 数组中。然后它在最后一个历史条上循环遍历数组,为每个枢轴点创建一个标签,并用线连接枢轴点:
//@version=5
indicator("Pivot Points High", overlay = true)
int legsInput = input(10)
// Define the `pivotPoint` UDT containing the time and price of pivots.
type pivotPoint
int openTime
float level
// Create an empty `pivotPoint` array.
var pivotHighArray = array.new<pivotPoint>()
// Detect new pivots (`na` is returned when no pivot is found).
pivotHighPrice = ta.pivothigh(legsInput, legsInput)
// Add a new `pivotPoint` object to the end of the array for each detected pivot.
if not na(pivotHighPrice)
// A new pivot is found; create a new object of `pivotPoint` type, setting its `openTime` and `level` fields.
newPivot = pivotPoint.new(time[legsInput], pivotHighPrice)
// Add the new pivot object to the array.
array.push(pivotHighArray, newPivot)
// On the last historical bar, draw pivot labels and connecting lines.
if barstate.islastconfirmedhistory
var pivotPoint previousPoint = na
for eachPivot in pivotHighArray
// Display a label at the pivot point.
label.new(eachPivot.openTime, eachPivot.level, str.tostring(eachPivot.level, format.mintick), xloc.bar_time, textcolor = color.white)
// Create a line between pivots.
if not na(previousPoint)
// Only create a line starting at the loop's second iteration because lines connect two pivots.
line.new(previousPoint.openTime, previousPoint.level, eachPivot.openTime, eachPivot.level, xloc = xloc.bar_time)
// Save the pivot for use in the next iteration.
previousPoint := eachPivot
复制对象
在 Pine 中,对象是通过引用来分配的。当将现有对象分配给新变量时,两者都指向同一个对象。
在下面的例子中,我们创建一个pivot1
对象并将其x
字段设置为 1000。然后,我们声明一个pivot2
包含对该对象的引用的变量pivot1
,因此两者都指向同一个实例。
pivot2.x
因此更改也会更改pivot1.x
,因为两者都引用x
同一个对象的字段:
//@version=5
indicator("")
type pivotPoint
int x
float y
pivot1 = pivotPoint.new()
pivot1.x := 1000
pivot2 = pivot1
pivot2.x := 2000
// Both plot the value 2000.
plot(pivot1.x)
plot(pivot2.x)
copy()
要创建独立于原始对象的副本,在这种情况下我们可以使用内置方法。
在此示例中,我们声明pivot2
引用对象复制实例的变量pivot1
。现在,更改pivot2.x
不会改变pivot1.x
,因为它引用的是x
单独对象的字段:
//@version=5
indicator("")
type pivotPoint
int x
float y
pivot1 = pivotPoint.new()
pivot1.x := 1000
pivot2 = pivotPoint.copy(pivot1)
pivot2.x := 2000
// Plots 1000 and 2000.
plot(pivot1.x)
plot(pivot2.x)
值得注意的是,内置copy()
方法会生成
对象的浅表副本。如果对象具有特殊类型的字段
(array、
matrix、
map、
line、
linefill、
box、
polyline、
label、
table或
chart.point),则对象的浅表副本中的这些字段将指向与原始对象相同的实例。
在以下示例中,我们定义了一个InfoLabel
类型,其中标签是其字段之一。脚本实例化对象shallow
的副本parent
,然后调用用户定义的set()
方法来更新
每个对象的info
和lbl
字段。由于lbl
两个对象的字段指向同一个标签实例,因此对任一对象中此字段的更改都会影响另一个对象:
//@version=5
indicator("Shallow Copy")
type InfoLabel
string info
label lbl
method set(InfoLabel this, int x = na, int y = na, string info = na) =>
if not na(x)
this.lbl.set_x(x)
if not na(y)
this.lbl.set_y(y)
if not na(info)
this.info := info
this.lbl.set_text(this.info)
var parent = InfoLabel.new("", label.new(0, 0))
var shallow = parent.copy()
parent.set(bar_index, 0, "Parent")
shallow.set(bar_index, 1, "Shallow Copy")
为了生成一个对象的深层副本,其中所有特殊类型字段都指向独立的实例,我们还必须明确复制这些字段。
在此示例中,我们定义了一个deepCopy()
方法,该方法实例化一个新InfoLabel
对象,其lbl
字段指向原始字段的副本。对deep
副本lbl
字段的更改不会影响该parent
对象,因为它指向一个单独的实例:
//@version=5
indicator("Deep Copy")
type InfoLabel
string info
label lbl
method set(InfoLabel this, int x = na, int y = na, string info = na) =>
if not na(x)
this.lbl.set_x(x)
if not na(y)
this.lbl.set_y(y)
if not na(info)
this.info := info
this.lbl.set_text(this.info)
method deepCopy(InfoLabel this) =>
InfoLabel.new(this.info, this.lbl.copy())
var parent = InfoLabel.new("", label.new(0, 0))
var deep = parent.deepCopy()
parent.set(bar_index, 0, "Parent")
deep.set(bar_index, 1, "Deep Copy")
阴影
为了避免将来添加到 Pine Script™ 的命名空间与现有脚本中的 UDT 或对象名称发生冲突,通常,UDT 和对象名称会遮盖语言的命名空间。例如,UDT 或对象可以使用内置类型的名称,例如 line 或 table。
只有该语言的五种原始类型不能用于命名 UDT 或对象: int、 float、 string、 bool和 color。