变量声明
介绍
变量是保存值的标识符。在使用之前,必须在代码中声明它们。变量声明的语法是:
或者
在哪里:
|
表示“或”,方括号([]
)内的部分可以出现零次或一次。- <declaration_mode> 是变量的 声明模式。它可以是 var 或 varip,或者什么都不做。
- <type> 是可选的,就像几乎所有的 Pine Script™ 变量声明一样(参见类型)。
- <identifier> 是变量的 名称。
- <expression> 可以是文字、变量、表达式或函数调用。
- <structure> 可以是 if、 for、 while 或 switch 结构。
- <tuple_declaration> 是用方括号 ( ) 括起来的变量名的逗号分隔列表
[]
,例如[ma, upperBand, lowerBand]
。
这些都是有效的变量声明。最后一个需要四行:
BULL_COLOR = color.lime
i = 1
len = input(20, "Length")
float f = 10.5
closeRoundedToTick = math.round_to_mintick(close)
st = ta.supertrend(4, 14)
var barRange = float(na)
var firstBarOpen = open
varip float lastClose = na
[macdLine, signalLine, histLine] = ta.macd(close, 12, 26, 9)
plotColor = if close > open
color.green
else
color.red
变量声明的正式语法是:
使用“na”进行初始化
在大多数情况下,显式类型声明是多余的,因为类型是在编译时根据右侧的值自动推断出来的=
,因此是否使用它们通常取决于个人喜好。例如:
baseLine0 = na // compile time error!
float baseLine1 = na // OK
baseLine2 = float(na) // OK
在示例的第一行中,编译器无法确定baseLine0
变量的类型,因为
na是没有特定类型的泛型值。baseLine1
变量的声明是正确的,因为其
浮点
类型是明确声明的。baseLine2
变量的声明也是正确的,因为其类型可以从表达式 中得出
,该表达式是将nafloat(na)
值显式转换
为
浮点类型。和
的声明是等效的。baseLine1
baseLine2
元组声明
函数调用或结构允许返回多个值。当我们调用它们并想要存储它们返回的值时,必须使用元组声明,它是用逗号分隔的一个或多个值集合,括在括号中。这允许我们同时声明多个变量。例如, 布林带的内置函数ta.bb()返回三个值:
[bbMiddle, bbUpper, bbLower] = ta.bb(close, 5, 4)
变量重新赋值
变量重新赋值是使用 := 重新赋值运算符完成的。只有在首先声明变量并赋予初始值后才能进行此操作。在计算中,通常需要为变量重新赋值,并且当必须从结构的本地块中为全局范围的变量赋新值时,也总是需要这样做,例如:
//@version=5
indicator("", "", true)
sensitivityInput = input.int(2, "Sensitivity", minval = 1, tooltip = "Higher values make color changes less sensitive.")
ma = ta.sma(close, 20)
maUp = ta.rising(ma, sensitivityInput)
maDn = ta.falling(ma, sensitivityInput)
// On first bar only, initialize color to gray
var maColor = color.gray
if maUp
// MA has risen for two bars in a row; make it lime.
maColor := color.lime
else if maDn
// MA has fallen for two bars in a row; make it fuchsia.
maColor := color.fuchsia
plot(ma, "MA", maColor, 2)
注意:
- 我们
maColor
仅在第一个条形图上进行初始化,因此它会在各个条形图上保留其值。 - 在每个条形图上,
if
语句都会检查 MA 是否在用户指定的条形图数量(默认值为 2)内上升或下降。发生这种情况时,必须在if
maColor
本地块内为 的值重新分配一个新值 。为此,我们使用 :=重新赋值运算符。 - 如果我们不使用
:=重新赋值运算符,其效果是初始化一个
maColor
与全局范围同名的新局部变量,但实际上是一个非常令人困惑的独立实体,它只会在本地块的长度内持续存在,然后消失得无影无踪。
Pine Script™ 中的所有用户定义变量都是可变的,这意味着可以使用 := 重新赋值运算符更改它们的值。为变量分配新值可能会更改其类型限定符(有关更多信息,请参阅 Pine Script™ 的类型系统页面 )。在脚本在一个栏上执行期间,可以根据需要多次为变量分配新值,因此脚本可以包含一个变量的任意次数的重新赋值。变量的 声明模式决定了如何保存分配给变量的新值。
声明模式
要了解声明模式对变量行为的影响,需要先了解 Pine Script™ 的 执行模型。
声明变量时,如果指定了声明模式,则必须先指定声明模式。可以使用三种模式:
在每个条形图
当没有指定明确的声明模式时,即没有 使用var或 varip 关键字,则在每个条上声明和初始化变量,例如,本页介绍中第一组示例中的以下声明:
BULL_COLOR = color.lime
i = 1
len = input(20, "Length")
float f = 10.5
closeRoundedToTick = math.round_to_mintick(close)
st = ta.supertrend(4, 14)
[macdLine, signalLine, histLine] = ta.macd(close, 12, 26, 9)
plotColor = if close > open
color.green
else
color.red
`var`
当 使用var 关键字时,变量仅初始化一次,如果声明在全局范围内,则在第一个柱上初始化一次;如果声明在本地块内,则在第一次执行本地块时初始化一次。此后,它将在连续的柱上保留其最后一个值,直到我们为其重新分配新值。这种行为在许多情况下非常有用,在这些情况下,变量的值必须在脚本在连续的柱上的迭代中保持不变。例如,假设我们想计算图表上的绿色柱的数量:
//@version=5
indicator("Green Bars Count")
var count = 0
isGreen = close >= open
if isGreen
count := count + 1
plot(count)
如果没有修饰符,每次新的条形更新触发脚本重新计算时var
,变量都会被重置为零(从而失去其值)。count
仅在第一个柱上声明变量通常有助于更有效地管理绘图。假设我们想将最后一个柱的 收盘 线延伸到右侧图表的右侧。我们可以这样写:
//@version=5
indicator("Inefficient version", "", true)
closeLine = line.new(bar_index - 1, close, bar_index, close, extend = extend.right, width = 3)
line.delete(closeLine[1])
但这样做效率不高,因为我们要在每个历史柱和实时柱的每次更新中创建和删除该线。使用以下方法效率更高:
//@version=5
indicator("Efficient version", "", true)
var closeLine = line.new(bar_index - 1, close, bar_index, close, extend = extend.right, width = 3)
if barstate.islast
line.set_xy1(closeLine, bar_index - 1, close)
line.set_xy2(closeLine, bar_index, close)
注意:
- 我们
closeLine
仅在第一根柱子上进行初始化,使用 var 声明模式 - 我们将更新该线的代码封装在if barstate.islast结构中,从而将其余代码的执行限制在图表的最后一条柱线上 。
使用var声明模式会略微降低性能 。因此,在声明常量时,如果性能是一个问题,最好不要使用 var,除非初始化涉及的计算时间比维护成本更长,例如,具有复杂代码或字符串操作的函数。
`varip`
要理解使用varip声明模式的变量行为, 需要事先了解 Pine Script™ 的 执行模型和 条状态。
varip关键字 可用于声明逃避回滚过程的变量,这在 Pine Script™ 的 执行模型页面中有解释。
脚本仅在历史条形图收盘时执行一次,而当脚本实时运行时,每次图表的 feed 检测到价格或成交量更新时,脚本都会执行。每次实时更新时,Pine Script™ 的运行时通常会将脚本变量的值重置为其上次提交的值,即上一个条形图收盘时的值。这通常很方便,因为每次实时脚本执行都从已知状态开始,从而简化了脚本逻辑。
然而,有时脚本逻辑需要代码能够在实时栏的 不同执行之间保存变量值。使用varip声明变量可以实现这一点。varip中 的“ip” 代表intrabar persist。
我们来看看下面的代码,它没有使用 varip:
//@version=5
indicator("")
int updateNo = na
if barstate.isnew
updateNo := 1
else
updateNo := updateNo + 1
plot(updateNo, style = plot.style_circles)
在历史柱上,
barstate.isnew
始终为真,因此绘图显示值“1”,因为ifelse
结构
的部分
从未执行过。在实时柱上,
barstate.isnew
仅在脚本首次在柱的“开盘”处执行时才为
真
。然后绘图将短暂显示“1”,直到后续执行发生。在实时柱期间的下一次执行中,将
执行if
语句的第二个分支,因为
barstate.isnew
不再为真。由于在每次执行时都初始化为
na ,表达式得出
na,因此在脚本的进一步实时执行中不会绘制任何内容。updateNo
updateNo + 1
如果我们现在使用
varip
来声明updateNo
变量,脚本的行为会非常不同:
//@version=5
indicator("")
varip int updateNo = na
if barstate.isnew
updateNo := 1
else
updateNo := updateNo + 1
plot(updateNo, style = plot.style_circles)
现在的区别是updateNo
跟踪每个实时条上发生的实时更新次数。之所以会发生这种情况,是因为
varip
声明允许在实时更新之间保留值updateNo
;它不再在每次实时执行脚本时回滚。barstate.isnew 上的测试
允许
我们在新的实时条出现时重置更新计数。
由于 varip 仅影响实时柱状图中代码的行为,因此,使用基于 varip 变量的逻辑设计的策略的回测结果将无法在历史柱状图上重现该行为,这将使测试结果无效。这也意味着历史柱状图上的绘图将无法实时重现脚本的行为。