Skip to content

Latest commit

 

History

History
254 lines (202 loc) · 14.9 KB

Loops.md

File metadata and controls

254 lines (202 loc) · 14.9 KB

循环

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>] <identifier> = ]for <identifier> = <expression> to <expression>[ by <expression>]
    <local_block_loop>

在哪里:

  • 方括号 ( []) 中的部分可以出现零次或一次,大括号 ( {}) 中的部分可以出现零次或多次。
  • <declaration_mode> 是变量的声明模式
  • 是可选的,就像在几乎所有 Pine Script™ 变量声明中一样(请参阅types
  • 是变量的名称
  • <表达式> 可以是文字、变量、表达式或函数调用。
  • <local_block_loop> 由零个或多个语句组成,后跟一个返回值,该返回值可以是值的元组。它必须缩进四个空格或制表符。它可以包含break退出循环的语句,或continue退出当前迭代并继续下一个迭代的语句。
  • 分配给该变量的值是<local_block_loop>的返回值,即循环最后一次迭代计算的最后一个值,或者如果循环未执行则为na 。
  • 中的标识符是循环的计数器初始值for <identifier>
  • 中的表达式是计数器的起始值。= <expression>
  • 中的表达式是计数器的*最终值。*它仅在进入循环时才被评估to <expression>
  • 中的表达式是可选的。这是循环计数器在循环的每次迭代中增加或减少的步骤。当 时,其默认值为 1 。时为-1 。用作默认值的步长(+1 或-1)由开始值和结束值确定。by <expression>``start value < end value``start value > end value

此示例使用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>] <identifier> = ]while <expression>
    <local_block_loop>

在哪里:

  • 方括号 ( []) 中的部分可以出现零次或一次。
  • <declaration_mode> 是变量的声明模式
  • 是可选的,就像在几乎所有 Pine Script™ 变量声明中一样(请参阅types
  • 是变量的名称
  • <表达式> 可以是文字、变量、表达式或函数调用。它在循环的每次迭代时进行评估。当它的计算结果为 时true,循环就会执行。当它求值时false循环停止。请注意,表达式的计算仅在每次迭代之前完成。循环内表达式值的更改只会影响下一次迭代。
  • <local_block_loop> 由零个或多个语句组成,后跟一个返回值,该返回值可以是值的元组。它必须缩进四个空格或制表符。它可以包含break退出循环的语句,或continue退出当前迭代并继续下一个迭代的语句。
  • 分配给变量的值是<local_block_loop>的返回值,即循环最后一次迭代计算的最后一个值,或者如果循环没有执行则为na 。

这是使用 while结构而不是 for one 编写的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的本地块i内显式递增 1 。
  • 我们使用+= 运算符将计数器加一。相当于.lowerBars += 1``lowerBars := lowerBars + 1

让我们使用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()函数中,该函数接受必须计算其阶乘的值作为参数。我们已经习惯了声明函数的参数,这表示如果调用函数时不带参数(如 in ),则参数将初始化为na ,这将阻止while循环的执行,因为其表达式将返回na。因此, while结构会将变量初始化为na。反过来,因为 的初始化是函数本地块的返回值,所以该函数将返回naint val = na``factorial()``val``counter > 0``result``result
  • 请注意while本地块的最后一行: fact。它是本地块的返回值,即while 结构最后一次迭代时的值。
  • 我们的初始化result不是必需的;我们这样做是为了可读性。我们也可以使用:
while counter > 0
    fact := fact * counter
    counter := counter - 1
    fact