限制
介绍
正如我们的欢迎页面中提到的:
由于每个脚本都会使用云端的计算资源,因此我们必须施加限制,以便在用户之间公平共享这些资源。我们努力设置尽可能少的限制,但当然必须实施尽可能多的限制,以确保平台顺利运行。限制适用于从其他符号请求的数据量、执行时间、内存使用量和脚本大小。
如果您使用 Pine Script™ 开发复杂的脚本,迟早会遇到我们施加的一些限制。本节概述了您可能遇到的限制。目前,Pine Script™ 程序员无法获取其脚本所消耗资源的数据。我们希望这种情况将来会有所改变。
同时,当您考虑大型项目时,最安全的做法是进行概念验证,以评估脚本在项目后期遇到限制的可能性。
下面,我们描述了 Pine Script™ 环境中施加的限制。
时间
脚本编译
脚本在图表上执行之前必须先编译。当您从 Pine 编辑器保存脚本或将脚本添加到图表时,就会发生编译。编译时间限制为两分钟,这取决于脚本的大小和复杂性,以及是否有以前编译的缓存版本可用。当编译超过两分钟的限制时,会发出警告。请注意该警告,缩短脚本,因为在连续三次警告后,将强制禁止一小时的编译尝试。优化代码时要考虑的第一件事是通过使用函数来封装经常使用的段来避免重复,并调用函数而不是重复代码。
脚本执行
脚本编译完成后即可执行。请参阅触发 脚本执行的事件,了解触发脚本执行的事件列表。脚本在数据集的所有条形图上执行所需的时间因账户类型而异。基本账户的限制为 20 秒,其他账户的限制为 40 秒。
循环执行
任何单个条形图上的任何循环的执行时间限制为 500 毫秒。嵌入式循环的外层循环算作一个循环,因此它将首先超时。请记住,即使循环可以在给定条形图上的 500 毫秒时间限制内执行,但在数据集的所有条形图上执行所需的时间仍可能导致您的脚本超出总执行时间限制。例如,总执行时间的限制将使您的脚本无法在 20,000 条形图数据集的每个条形图上执行 400 毫秒的循环,因为您的脚本将需要 8000 秒才能执行。
图表视觉效果
地块限制
每个脚本最多允许 64 个绘图计数。生成绘图计数的函数包括:
以下函数不会生成地块计数:
一次函数调用最多可以生成 7 个绘图计数,具体取决于函数及其调用方式。当您的脚本超过 64 个绘图计数的最大值时,运行时错误消息将显示脚本生成的绘图计数。一旦达到该点,您就可以通过在脚本中注释掉函数调用来确定函数调用生成的绘图计数数量。只要您的脚本仍然抛出错误,您就能够看到在注释掉一行之后实际绘图计数如何减少。
以下示例显示了不同的函数调用以及每个函数调用将生成的绘图计数数量:
//@version=5
indicator("Plot count example")
bool isUp = close > open
color isUpColor = isUp ? color.green : color.red
bool isDn = not isUp
color isDnColor = isDn ? color.red : color.green
// Uses one plot count each.
p1 = plot(close, color = color.white)
p2 = plot(open, color = na)
// Uses two plot counts for the `close` and `color` series.
plot(close, color = isUpColor)
// Uses one plot count for the `close` series.
plotarrow(close, colorup = color.green, colordown = color.red)
// Uses two plot counts for the `close` and `colorup` series.
plotarrow(close, colorup = isUpColor)
// Uses three plot counts for the `close`, `colorup`, and the `colordown` series.
plotarrow(close - open, colorup = isUpColor, colordown = isDnColor)
// Uses four plot counts for the `open`, `high`, `low`, and `close` series.
plotbar(open, high, low, close, color = color.white)
// Uses five plot counts for the `open`, `high`, `low`, `close`, and `color` series.
plotbar(open, high, low, close, color = isUpColor)
// Uses four plot counts for the `open`, `high`, `low`, and `close` series.
plotcandle(open, high, low, close, color = color.white, wickcolor = color.white, bordercolor = color.purple)
// Uses five plot counts for the `open`, `high`, `low`, `close`, and `color` series.
plotcandle(open, high, low, close, color = isUpColor, wickcolor = color.white, bordercolor = color.purple)
// Uses six plot counts for the `open`, `high`, `low`, `close`, `color`, and `wickcolor` series.
plotcandle(open, high, low, close, color = isUpColor, wickcolor = isUpColor , bordercolor = color.purple)
// Uses seven plot counts for the `open`, `high`, `low`, `close`, `color`, `wickcolor`, and `bordercolor` series.
plotcandle(open, high, low, close, color = isUpColor, wickcolor = isUpColor , bordercolor = isUp ? color.lime : color.maroon)
// Uses one plot count for the `close` series.
plotchar(close, color = color.white, text = "|", textcolor = color.white)
// Uses two plot counts for the `close`` and `color` series.
plotchar(close, color = isUpColor, text = "—", textcolor = color.white)
// Uses three plot counts for the `close`, `color`, and `textcolor` series.
plotchar(close, color = isUpColor, text = "O", textcolor = isUp ? color.yellow : color.white)
// Uses one plot count for the `close` series.
plotshape(close, color = color.white, textcolor = color.white)
// Uses two plot counts for the `close` and `color` series.
plotshape(close, color = isUpColor, textcolor = color.white)
// Uses three plot counts for the `close`, `color`, and `textcolor` series.
plotshape(close, color = isUpColor, textcolor = isUp ? color.yellow : color.white)
// Uses one plot count.
alertcondition(close > open, "close > open", "Up bar alert")
// Uses one plot count.
bgcolor(isUp ? color.yellow : color.white)
// Uses one plot count for the `color` series.
fill(p1, p2, color = isUpColor)
此示例生成了 56 个绘图计数。如果我们再添加两个对 plotcandle()的最后调用实例,脚本将抛出一个错误,指出脚本现在使用了 70 个绘图计数,因为每次额外调用 plotcandle() 都会生成七个绘图计数,而 56 + (7 * 2) 等于 70。
线、框、折线和标签限制
与可以覆盖图表整个数据集的绘图相反,脚本默认
只会在图表上显示最后 50 条
线、
框、
折线和标签。可以通过脚本的
indicator()
或
strategies()声明语句的、、
和参数增加每种绘图类型的最大数量。线、
框和
标签ID
的最大数量
为 500,折线ID
的最大数量
为 100。max_lines_count
max_boxes_count
max_polylines_count
max_labels_count
在此示例中,我们将图表上显示的最近标签的最大数量设置为 100:
//@version=5
indicator("Label limits example", max_labels_count = 100, overlay = true)
label.new(bar_index, high, str.tostring(high, format.mintick))
需要注意的是,将任何绘图对象的属性设置为
na时,其 ID 仍然存在,因此会影响脚本的绘图总数。为了演示此行为,以下脚本
在每个条形上绘制“买入”和“卖出”标签x
,其值由longCondition
和
shortCondition
变量确定。
当条形索引为偶数时,“买入”标签的x
值为
na ,当条形索引为奇数时, “卖出”标签的x
值为
namax_labels_count
。虽然此示例中的值为 10,但我们可以看到脚本
在图表上
显示的标签少于 10 个,因为值为na 的
标签也计入总数:
//@version=5
// Approximate maximum number of label drawings
MAX_LABELS = 10
indicator("labels with na", overlay = false, max_labels_count = MAX_LABELS)
// Add background color for the last MAX_LABELS bars.
bgcolor(bar_index > last_bar_index - MAX_LABELS ? color.new(color.green, 80) : na)
longCondition = bar_index % 2 != 0
shortCondition = bar_index % 2 == 0
// Add "Buy" and "Sell" labels on each new bar.
label.new(longCondition ? bar_index : na, 0, text = "Buy", color = color.new(color.green, 0), style = label.style_label_up)
label.new(shortCondition ? bar_index : na, 0, text = "Sell", color = color.new(color.red, 0), style = label.style_label_down)
plot(longCondition ? 1 : 0)
plot(shortCondition ? 1 : 0)
为了显示所需数量的标签,我们必须消除我们不想显示的标签绘制,而不是将其属性设置为 na。下面的示例使用 if 结构有条件地绘制“Buy”和“Sell”标签,防止脚本在不需要时创建新的标签 ID:
//@version=5
// Approximate maximum number of label drawings
MAX_LABELS = 10
indicator("conditional labels", overlay = false, max_labels_count = MAX_LABELS)
// Add background color for the last MAX_LABELS bars.
bgcolor(bar_index > last_bar_index - MAX_LABELS ? color.new(color.green, 80) : na)
longCondition = bar_index % 2 != 0
shortCondition = bar_index % 2 == 0
// Add a "Buy" label when `longCondition` is true.
if longCondition
label.new(bar_index, 0, text = "Buy", color = color.new(color.green, 0), style = label.style_label_up)
// Add a "Sell" label when `shortCondition` is true.
if shortCondition
label.new(bar_index, 0, text = "Sell", color = color.new(color.red, 0), style = label.style_label_down)
plot(longCondition ? 1 : 0)
plot(shortCondition ? 1 : 0)
表限制
脚本最多可以在图表上显示九个 表格,每个可能的位置一个: position.bottom_center、 position.bottom_left、 position.bottom_right、 position.middle_center、 position.middle_left、 position.middle_right、 position.top_center、 position.top_left和 position.top_right。当尝试将两个表格放在同一位置时,图表上只会显示最新的实例。
`request.*()`调用
呼叫次数
脚本不能包含超过 40 个对
request.()
命名空间中函数的调用。这些函数的所有实例都计入此限制,即使包含在
脚本主逻辑未使用的用户定义函数的本地块中也是如此。此限制适用于其他时间范围和数据页面中讨论的所有函数
,包括:
- 请求.安全()
- 请求.security_lower_tf()
- 请求.currency_rate()
- 请求.股息()
- 请求.splits()
- 请求.收入()
- 请求.quandl()
- 请求.财务()
- 请求.经济()
- 请求.种子()
内部条形码
脚本可以通过request.security() 或 request.security_lower_tf()函数检索最多最近的 100,000 个intrabar (较低时间范围的条形图) 。
100,000 个内部条形图覆盖的图表时间范围内的条形图数量随每个图表条形图包含的内部条形图数量而变化。例如,在 60 分钟图表上运行脚本时请求 1 分钟时间范围内的数据意味着每个图表条形图最多可以包含 60 个内部条形图。在这种情况下,内部条形图请求覆盖的图表条形图的最小数量为 1,666,因为 100,000 / 60 = 1,666.67。但需要注意的是,提供商可能不会报告 一小时内每分钟的数据。因此,这样的请求可能会覆盖更多的图表条形图,具体取决于可用的数据。
元组元素限制
脚本中的所有request.*()
函数调用加起来不能返回超过 127 个元组元素。当所有
request.*()
调用的组合元组大小超过 127 个元素时,可以使用
用户定义类型 (UDT)来请求更多数量的值。
下面的示例概述了此限制及其解决方法。第一个
request.security()
调用表示使用包含 128 个元素的元组作为expression
参数。由于元素数量大于 127,因此会导致错误。
为了避免错误,我们可以使用相同的值作为UDT
对象内的
字段,
并将其 ID 传递给:expression
//@version=5
indicator("Tuple element limit")
s1 = close
s2 = close * 2
...
s128 = close * 128
// Causes an error.
[v1, v2, v3, ..., v128] = request.security(syminfo.tickerid, "1D", [s1, s2, s3, ..., s128])
// Works fine:
type myType
float v1
float v2
float v3
...
float v128
myObj = request.security(syminfo.tickerid, "1D", myType.new(s1, s2, s3, ..., s128))
注意:
- 此示例概述了一种场景,其中脚本尝试在单个request.security()调用中评估 128 个元组元素 。如果我们将元组请求拆分到多个调用中,则同样的限制也适用。例如,两个 request.security() 调用,每个调用都检索一个包含 64 个元素的元组,也会导致错误。
脚本大小和内存
编译后的token
在执行脚本之前,编译器会将其转换为标记化的中间语言(IL)。使用 IL 允许 Pine Script™ 通过应用各种内存和性能优化来容纳更大的脚本。编译器根据 IL 形式的标记数来确定脚本的大小,而不是根据 Pine Editor 中可查看的代码中的字符数或行数来确定脚本的大小。
每个指标、策略和库脚本的编译形式限制为 80,000 个标记。当脚本导入库时,所有导入库中的标记总数不能超过 100 万。无法检查脚本的编译形式,也无法检查其 IL 标记数。因此,只有当编译器达到该大小限制时,您才会知道脚本超出了大小限制。
在大多数情况下,脚本的编译大小可能不会达到限制。但是,如果编译后的脚本确实达到了 token 限制,那么减少编译 token 的最有效方法是减少重复代码、将冗余调用封装在函数中,并尽可能利用 库。
需要注意的是,编译过程会 从最终的 IL 形式中忽略任何未使用的变量、函数、类型等,其中“未使用”是指不影响脚本输出的任何内容。此优化可防止代码中的多余元素影响脚本的 IL 标记计数。
例如,下面的脚本声明了一个 用户定义类型和一个 用户定义方法,并使用它们定义了一系列调用:
//@version=5
indicator("My Script")
plot(close)
type myType
float field = 10.0
method m(array<myType> a, myType v) =>
a.push(v)
var arr = array.new<myType>()
arr.push(myType.new(25))
arr.m(myType.new())
尽管脚本中包含了array.new<myType>()、
myType.new()
和调用,但脚本实际输出的唯一内容是。其余代码不会影响输出。因此,此脚本的编译形式将具有与以下相同的标记数:arr.m()
plot(close)
//@version=5
indicator("My Script")
plot(close)
每个作用域的变量
脚本的每个作用域最多可包含 1,000 个变量。Pine 脚本始终包含一个全局作用域(由非缩进代码表示),并且可能包含零个或多个局部作用域。局部作用域是缩进代码的一部分,表示在 函数和 方法以及 if、 switch、 for、 for…in和 while结构中执行的过程 ,这些结构允许一个或多个局部块。每个局部块算作一个局部作用域。
使用?:三元运算符的条件表达式的分支 不算作本地块。
范围计数
脚本中的作用域总数(包括其全局作用域和 其使用的用户定义函数、 方法、 条件结构的每个局部作用域)不能超过 500 个。
需要注意的是,
request.security()、
request.security_lower_tf()和
request.seed()
函数会复制在另一个上下文中评估其expression
参数值所需的范围。每次调用这些request.*()
函数产生的范围也计入脚本的范围限制。
例如,假设我们创建了一个脚本,其中有一个全局变量,该变量依赖于 250 个 if 结构的本地作用域。此脚本的总作用域数为251(1 个全局作用域 + 250 个本地作用域):
//@version=5
indicator("Scopes demo")
var x = 0
if close > 0
x += 0
if close > 1
x += 1
// ... Repeat this `if close > n` pattern until `n = 249`.
if close > 249
x += 249
plot(x)
由于作用域的总数在限制之内,因此它将成功编译。现在,假设我们调用
request.security()x
来从另一个上下文
评估 的值并绘制其值。在这种情况下,它将有效地使脚本的作用域数量翻倍,因为 的值x
取决于所有脚本的作用域:
//@version=5
indicator("Scopes demo")
var x = 0
if close > 0
x += 0
if close > 1
x += 1
// ... Repeat this `if close > n` pattern until `n = 249`.
if close > 249
x += 249
plot(x)
plot(request.security(syminfo.tickerid, "1D", x)) // Causes compilation error since the scope count is now 502.
我们可以通过将 if 块封装在 用户定义函数中来解决此问题,因为函数的作用域算作一个嵌入作用域:
//@version=5
indicator("Scopes demo")
f() =>
var x = 0
if close > 0
x += 0
if close > 1
x += 1
// ... Repeat this `if close > n` pattern until `n = 249`.
if close > 249
x += 249
plot(f())
plot(request.security(syminfo.tickerid, "1D", f())) // No compilation error.
编译请求大小
脚本的编译请求大小不能超过 5MB。编译请求是发送给编译器的所有信息。这些信息包括脚本本身以及脚本导入的任何库。
与已编译令牌的限制不同,请求大小限制包括未使用的代码部分。这是因为脚本尚未编译,因此任何未使用的代码尚未被优化掉。
为了减少编译请求的大小,您可以:
- 通过优化代码来减少脚本的大小。
- 减少脚本输入的数量(脚本输入单独统计)。
- 删除所有不需要的导入库。
- 使用较小的库。无论调用哪些函数,整个库都会被发送进行编译。
收藏
Pine Script™ 集合(数组和映射)最多可包含 100,000 个元素。映射中的每个键值对包含两个元素,这意味着映射最多可包含 50,000 个键值对。
其他限制
最大条数返回
使用[]历史引用运算符引用过去值
取决于 Pine Script™ 运行时维护的历史缓冲区的大小,该缓冲区的最大大小限制为 5000 条。此帮助中心页面max_bars_back
讨论了历史缓冲区以及如何使用参数或
max_bars_back()函数
更改其大小
。
最大前进杆数
使用 定位绘图时xloc.bar_index
,可以使用大于当前柱的柱索引值作为x
坐标。最多可以引用未来 500 个柱。
此示例显示了如何在input.int()函数调用中使用 [maxval] 参数 来限制用户定义的条数,然后我们绘制一条投影线,以使其永远不会超过限制:
//@version=5
indicator("Max bars forward example", overlay = true)
// This function draws a `line` using bar index x-coordinates.
drawLine(bar1, y1, bar2, y2) =>
// Only execute this code on the last bar.
if barstate.islast
// Create the line only the first time this function is executed on the last bar.
var line lin = line.new(bar1, y1, bar2, y2, xloc.bar_index)
// Change the line's properties on all script executions on the last bar.
line.set_xy1(lin, bar1, y1)
line.set_xy2(lin, bar2, y2)
// Input determining how many bars forward we draw the `line`.
int forwardBarsInput = input.int(10, "Forward Bars to Display", minval = 1, maxval = 500)
// Calculate the line's left and right points.
int leftBar = bar_index[2]
float leftY = high[2]
int rightBar = leftBar + forwardBarsInput
float rightY = leftY + (ta.change(high)[1] * forwardBarsInput)
// This function call is executed on all bars, but it only draws the `line` on the last bar.
drawLine(leftBar, leftY, rightBar, rightY)
图表条形图
图表上显示的条形图数量取决于图表符号和时间范围的可用历史数据量,以及您持有的账户类型。当所需的历史日期可用时,图表条形图的最小数量为:
- 高级计划有 20,000 条。
- Pro 和 Pro+ 计划有 10,000 条。
- 其他计划为 5000 条。
回测中的交易订单
回测策略时最多可下单 9000 笔。使用深度回测时,上限为 200,000 笔。