循环
介绍
当不需要循环时
Pine Script™ 的运行时及其内置函数使循环在许多情况下变得不必要。尚不熟悉 Pine Script™ 运行时和内置函数的新手 Pine Script™ 程序员如果想要计算最近 10 个 收盘 价的平均值,通常会编写如下代码:
//@version=5
indicator("Inefficient MA", "", true)
MA_LENGTH = 10
sumOfCloses = 0.0
for offset = 0 to MA_LENGTH - 1
sumOfCloses := sumOfCloses + close[offset]
inefficientMA = sumOfCloses / MA_LENGTH
plot(inefficientMA)
在 Pine 中,for循环 对于完成此类任务而言是不必要的,而且效率低下。应该这样做。此代码更短,运行速度更快,因为它不使用循环,而是使用 ta.sma() 内置函数来完成任务:
//@version=5
indicator("Efficient MA", "", true)
thePineMA = ta.sma(close, 10)
plot(thePineMA)
计算最后几条柱状图中某个条件的发生次数也是 Pine Script™ 初级程序员经常认为必须用循环来完成的任务。要计算最后 10 条柱状图中上涨的柱状图数量,他们将使用:
//@version=5
indicator("Inefficient sum")
MA_LENGTH = 10
upBars = 0.0
for offset = 0 to MA_LENGTH - 1
if close[offset] > open[offset]
upBars := upBars + 1
plot(upBars)
在 Pine 中编写此代码的有效方法(对于程序员来说,因为它可以节省时间、实现最快的加载图表以及最公平地共享我们的公共资源)是使用 math.sum () 内置函数来完成任务:
//@version=5
indicator("Efficient sum")
upBars = math.sum(close > open ? 1 : 0, 10)
plot(upBars)
那里发生的事情是:
- 我们使用 ?: 三元运算符来构建一个表达式,使得上行条的结果为 1,其他条的结果为 0。
- 我们使用 math.sum() 内置函数来对最后 10 个条形图的值进行运行总和。
何时需要循环
循环的存在是有充分理由的,因为即使在 Pine Script™ 中,在某些情况下它们也是必要的。这些情况通常包括:
- 集合(数组、 矩阵和 映射)的操作。
- 回顾历史,使用只能在当前柱上知道的参考值来分析柱,例如,找出过去有多少高点高于 当前柱的 高点。由于当前柱的高点 仅在脚本正在运行的柱上才为人所知,因此需要循环来回溯并分析过去的柱。
- 对过去的条形图执行无法使用内置函数完成的计算。
`为`
for结构 允许使用计数器重复执行语句。其语法为:
在哪里:
- 方括号 (
[]
) 中的部分可以出现零次或一次,花括号 ({}
) 中的部分可以出现零次或多次。 - <declaration_mode> 是变量的 声明模式
- <type> 是可选的,就像几乎所有的 Pine Script™ 变量声明一样(参见类型)
- <identifier> 是变量的 名称
- <expression> 可以是文字、变量、表达式或函数调用。
- <local_block_loop> 由零个或多个语句组成,后跟一个返回值,返回值可以是一组值。它必须缩进四个空格或一个制表符。它可以包含
break
退出循环的语句,也可以continue
包含退出当前迭代并继续下一个迭代的语句。 - 分配给该变量的值是 <local_block_loop> 的返回值,即循环最后一次迭代计算出的最后一个值, 如果未执行循环,则为na 。
- 中的标识符
for <identifier>
是循环的计数器初始值。 - 中的表达式
= <expression>
是计数器的起始值。 - 中的表达式
to <expression>
是计数器的最终值。它仅在循环入口处进行评估。 - 中的表达式
by <expression>
是可选的。它是循环计数器在每次循环迭代中增加或减少的步长。当 时,它的默认值为 1。当 时,start value < end value
它的默认值为 -1start value > end value
。用作默认值的步长(+1 或 -1)由起始值和结束值决定。
此示例使用 for 语句回顾用户定义的条形图数量,以确定有多少条形图的 最高 点高于或低于 图表上最后一条形图的 最高点。这里需要一个for循环,因为脚本只能访问图表最后一条形图上的参考值。Pine Script™ 的运行时在这里不能用于动态计算,因为脚本正在逐条执行:
//@version=5
indicator("`for` loop")
lookbackInput = input.int(50, "Lookback in bars", minval = 1, maxval = 4999)
higherBars = 0
lowerBars = 0
if barstate.islast
var label lbl = label.new(na, na, "", style = label.style_label_left)
for i = 1 to lookbackInput
if high[i] > high
higherBars += 1
else if high[i] < high
lowerBars += 1
label.set_xy(lbl, bar_index, high)
label.set_text(lbl, str.tostring(higherBars, "# higher bars\n") + str.tostring(lowerBars, "# lower bars"))
此示例在其函数中使用循环checkLinesForBreaches()
来遍历枢轴线数组,并在价格与枢轴线交叉时删除它们。此处需要使用循环,因为必须在每个条形上检查每个
hiPivotLines
和loPivotLines
数组中的所有线条,并且没有内置函数可以为我们执行此操作:
//@version=5
MAX_LINES_COUNT = 100
indicator("Pivot line breaches", "", true, max_lines_count = MAX_LINES_COUNT)
color hiPivotColorInput = input(color.new(color.lime, 0), "High pivots")
color loPivotColorInput = input(color.new(color.fuchsia, 0), "Low pivots")
int pivotLegsInput = input.int(5, "Pivot legs")
int qtyOfPivotsInput = input.int(50, "Quantity of last pivots to remember", minval = 0, maxval = MAX_LINES_COUNT / 2)
int maxLineLengthInput = input.int(400, "Maximum line length in bars", minval = 2)
// ————— Queues a new element in an array and de-queues its first element.
qDq(array, qtyOfElements, arrayElement) =>
array.push(array, arrayElement)
if array.size(array) > qtyOfElements
// Only deqeue if array has reached capacity.
array.shift(array)
// —————— Loop through an array of lines, extending those that price has not crossed and deleting those crossed.
checkLinesForBreaches(arrayOfLines) =>
int qtyOfLines = array.size(arrayOfLines)
// Don't loop in case there are no lines to check because "to" value will be `na` then`.
for lineNo = 0 to (qtyOfLines > 0 ? qtyOfLines - 1 : na)
// Need to check that array size still warrants a loop because we may have deleted array elements in the loop.
if lineNo < array.size(arrayOfLines)
line currentLine = array.get(arrayOfLines, lineNo)
float lineLevel = line.get_price(currentLine, bar_index)
bool lineWasCrossed = math.sign(close[1] - lineLevel) != math.sign(close - lineLevel)
bool lineIsTooLong = bar_index - line.get_x1(currentLine) > maxLineLengthInput
if lineWasCrossed or lineIsTooLong
// Line stays on the chart but will no longer be extend on further bars.
array.remove(arrayOfLines, lineNo)
// Force type of both local blocks to same type.
int(na)
else
line.set_x2(currentLine, bar_index)
int(na)
// Arrays of lines containing non-crossed pivot lines.
var array<line> hiPivotLines = array.new_line(qtyOfPivotsInput)
var array<line> loPivotLines = array.new_line(qtyOfPivotsInput)
// Detect new pivots.
float hiPivot = ta.pivothigh(pivotLegsInput, pivotLegsInput)
float loPivot = ta.pivotlow(pivotLegsInput, pivotLegsInput)
// Create new lines on new pivots.
if not na(hiPivot)
line newLine = line.new(bar_index[pivotLegsInput], hiPivot, bar_index, hiPivot, color = hiPivotColorInput)
line.delete(qDq(hiPivotLines, qtyOfPivotsInput, newLine))
else if not na(loPivot)
line newLine = line.new(bar_index[pivotLegsInput], loPivot, bar_index, loPivot, color = loPivotColorInput)
line.delete(qDq(loPivotLines, qtyOfPivotsInput, newLine))
// Extend lines if they haven't been crossed by price.
checkLinesForBreaches(hiPivotLines)
checkLinesForBreaches(loPivotLines)
`同时`
while 结构允许重复执行语句, 直到条件为假。其语法为:
在哪里:
- 方括号 (
[]
) 中的部分可以出现零次或一次。 - <declaration_mode> 是变量的 声明模式
- <type> 是可选的,就像几乎所有的 Pine Script™ 变量声明一样(参见类型)
- <identifier> 是变量的 名称
- <expression> 可以是文字、变量、表达式或函数调用。它在循环的每次迭代中被评估。当它评估为 时
true
,循环执行。当它评估为 时,循环false
停止。请注意,表达式的评估仅在每次迭代之前进行。对循环内表达式值的更改只会对下一次迭代产生影响。 - <local_block_loop> 由零个或多个语句组成,后跟一个返回值,返回值可以是一组值。它必须缩进四个空格或一个制表符。它可以包含
break
退出循环的语句,也可以continue
包含退出当前迭代并继续下一个迭代的语句。 - 分配给 <identifier> 变量的值是 <local_block_loop> 的返回值,即循环最后一次迭代计算出的最后一个值, 如果未执行循环,则为na 。
这是使用 while 结构而不是 for结构编写的for部分的第一个代码示例 :
//@version=5
indicator("`for` loop")
lookbackInput = input.int(50, "Lookback in bars", minval = 1, maxval = 4999)
higherBars = 0
lowerBars = 0
if barstate.islast
var label lbl = label.new(na, na, "", style = label.style_label_left)
// Initialize the loop counter to its start value.
i = 1
// Loop until the `i` counter's value is <= the `lookbackInput` value.
while i <= lookbackInput
if high[i] > high
higherBars += 1
else if high[i] < high
lowerBars += 1
// Counter must be managed "manually".
i += 1
label.set_xy(lbl, bar_index, high)
label.set_text(lbl, str.tostring(higherBars, "# higher bars\n") + str.tostring(lowerBars, "# lower bars"))
注意:
让我们使用while结构来计算阶乘函数 :
//@version=5
indicator("")
int n = input.int(10, "Factorial of", minval=0)
factorial(int val = na) =>
int counter = val
int fact = 1
result = while counter > 0
fact := fact * counter
counter := counter - 1
fact
// Only evaluate the function on the first bar.
var answer = factorial(n)
plot(answer)
注意:
- 我们使用
input.int()
作为输入,因为我们需要指定一个
minval
值来保护我们的代码。虽然 input() 也支持“int”类型值的输入,但它不支持参数minval
。 - 我们将脚本的功能打包成一个
factorial()
函数,该函数接受必须计算其阶乘的值作为参数。我们已使用 来int val = na
声明函数的参数,这表示如果调用函数时没有参数,如factorial()
,则val
参数将初始化为 na ,这将阻止while循环的执行, 因为其counter > 0
表达式将返回 na。 因此, while 结构将result
变量初始化为 na。反过来,由于 的初始化result
是我们函数本地块的返回值,因此函数将返回 na。 - 注意while局部块的最后一行
:。它是局部块的返回值,因此是 while
结构最后一次迭代时
fact
的值 。 - 初始化
result
不是必需的;我们这样做是为了提高可读性。我们也可以使用:
while counter > 0
fact := fact * counter
counter := counter - 1
fact