如何修复TradingView的”索引深度不足”错误
每一个TradingView指标或策略脚本都需要依赖一定数量的历史K线来进行计算。当TradingView无法自动判断出脚本到底需要多少根历史K线时,它就会触发’out of depth at index’(索引超出深度)这个错误。让我们来看看这个错误到底意味着什么,以及我们该如何修复它。
剖析TradingView的’out of depth at index’错误
几乎所有的TradingView脚本都会使用历史数据进行计算。我们使用的数据量决定了脚本在开始计算前需要等待多长时间。例如,一个绘制20周期最高价的指标,需要至少20根K线的数据才能开始工作。同样,一个基于9周期SMA均线进行交易的策略,也需要至少9根K线的数据才能发出第一笔订单。
在大多数情况下,TradingView都能很好地估算出我们的脚本需要多少根历史K线,并在数据足够之前暂停计算。但有时候,即便是最好的估算也会出错。当这种情况发生时,TradingView便会触发’out of depth at index’的错误信息。这个错误会显示在我们图表的顶部:
‘out of depth at index’错误是少数几个不会在Pine编辑器下方的控制台窗口中提供更多详细信息的错误之一。但这并不意味着修复这个错误很困难。
修复TradingView的’out of depth at index’错误
‘out of depth at index’这个错误信息可能听起来很抽象,有点吓人。但修复它其实只需要三个简单的步骤。第一步,在Pine编辑器中,打开触发该错误的指标或策略脚本。第二步,通读代码,估算你的脚本在计算中最多会回溯多少根历史K线,同时也要考虑到你设置的输入选项可能允许的最大值。第三步,在脚本的 indicator() 或 strategy() 函数中,添加 max_bars_back 这个参数,将它的值设为你估算出的脚本所需的最大K线回溯数量。如果你已经添加了 max_bars_back 参数但错误依然存在,那么只需逐步增加 max_bars_back 的值,然后保存脚本重试即可。
虽然每个脚本需要的回溯K线数都不同,但这里有一个快速的经验法则:对于短期指标和策略,将 max_bars_back 设为50通常就足够了;对于中期指标和策略,使用100是个不错的选择;对于长期脚本,我们可能需要将这个值设为200或更高。
如果你在一个并非由你编写的脚本中遇到这个错误,要弄清楚它到底需要多少根K线可能会很耗时。这里有一个快速解决问题的方法:首先,在 indicator() 或 strategy() 函数中添加 max_bars_back=25 并保存。如果错误依旧,将值增加25(即 max_bars_back=50)并再次保存。重复这个过程,不断增加 max_bars_back 的值,直到错误不再出现为止。
在我们看一个修复错误的具体例子之前,让我们先来理解一下最大回溯K线数到底是什么。
理解TradingView的max_bars_back
对于每一个脚本,TradingView都会尝试估算其计算所需的最大历史K线数量。比方说,我们要绘制一条10周期的EMA均线。在这种情况下,TradingView需要等待到图表上出现第10根K线后,才能开始绘制EMA。
这个所需的K线数量,就是我们脚本的最大回溯K线数。(这里的最大指的是,如果你的脚本同时绘制10周期EMA和50周期SMA,那么脚本至少需要等到第50根K线才能开始所有计算。)
但是,如果我们的脚本代码足够复杂,TradingView就可能无法准确地计算出这个数值。这种情况在使用 ta.pivothigh() 和 ta.pivotlow() 这类函数时尤其常见。
想象一下,我们想计算10周期的EMA,但TradingView错误地估计只需要5根K线。结果可想而知:脚本无法完成计算,并最终抛出’out of depth at index’的错误。
为了修复(或从一开始就避免)这个错误,我们可以使用 indicator() 和 strategy() 函数中的 max_bars_back 参数。通过这个参数,我们可以手动告诉TradingView,我们的脚本最多需要回溯多少根K线来进行所有计算。
重要提示:如果你为 max_bars_back 设定的值不够高,不足以让脚本成功完成计算,TradingView会忽略你设定的值,并再次尝试自己估算。但如果这次估算依然失败,脚本还是会报出’out of depth at index’的错误。这就解释了为什么即使我们已经设置了 max_bars_back 参数,有时仍然会遇到这个错误。
错误示例:绘制一个枢轴点指标
下面的示例指标试图在图表上绘制一个波段高点(swing pivot high)。但我们没有在 indicator() 函数中设置 max_bars_back 参数,而是让TradingView自己去估算。
让我们看看会发生什么:
//@version=5
indicator(title="枢轴点指标", overlay=true)
// 输入选项
pivOffsetA = input.int(7, title="枢轴点偏移量 A")
pivOffsetB = input.int(15, title="枢轴点偏移量 B")
// 判断枢轴点
emaFilter = close > ta.ema(close, 20)
pivHighValue = emaFilter ? pivOffsetA : pivOffsetB
pivHigh = ta.pivothigh(pivHighValue, pivHighValue[1])
// 在图表上绘制枢轴点
plot(emaFilter ? pivHigh : na, style=plot.style_circles,
linewidth=5, color=color.green, offset=-pivOffsetA)
plot(emaFilter == false ? pivHigh : na, style=plot.style_circles,
linewidth=5, color=color.green, offset=-pivOffsetB)
尽管代码并不算特别复杂,我们还是遇到了’out of depth at index’的错误。幸运的是,修复它并不难:通读代码,找到可能需要的最大回溯K线数,然后将这个值赋给 indicator() 函数的 max_bars_back 参数。
在第13行,我们使用 ta.pivothigh() 来计算波段高点。我们为其长度设置的最大值是15。然而,ta.pivothigh() 函数在判断一个点是否为枢轴高点时,既需要看它左边的K线,也需要看它右边的K线。因此,要计算一个左右各有15根K线的枢轴点,我们至少需要30根K线的数据。
为了保险起见,我们将 max_bars_back 设为40:
indicator(title="枢轴点指标", overlay=true, max_bars_back=40)
做了这个修改并保存脚本后,’out of depth at index’的错误就消失了,TradingView也能毫无问题地绘制出枢轴高点:
总结
每个TradingView指标或策略脚本都需要使用历史数据来进行计算。TradingView会自动估算我们的脚本需要回溯多少根K线,并且大多数时候这个估算是准确的。
但对于复杂的代码,TradingView的估算可能会出错。当它在没有足够历史数据的情况下尝试运行脚本时,就会产生’out of depth at index’的错误。
虽然这个错误的原因听起来很复杂,但修复它并不难。只需估算出你的脚本计算所需的最大K线数,然后将这个值赋给 indicator() 或 strategy() 函数的 max_bars_back 参数即可。
一个关于 max_bars_back 的经验法则是:短期指标和策略通常不需要超过50根历史K线;中期脚本最好使用100;而对于长期脚本,我们可能需要200或更多的K线。
如何处理TradingView的”脚本请求太多证券”错误?
TradingView的优势之一,是能轻松地加载其他交易品种或更高时间框架的价格数据。但如果过度使用这一优势,我们便会遇到’script requests too many securities’(脚本请求过多证券)的错误。本文将探讨如何防止这类错误的发生。
探究TradingView的请求过多证券错误
在TradingView Pine中,加载另一个交易品种和/或时间框架的数据非常简单:只需执行 request.security() 函数,并提供你想要的品种代码、时间框架和数据类型即可。得益于TradingView的数据已全部存储于云端,我们无需自己下载和管理数据。
当然,这其中存在一些实际的限制:即便是强大的计算机服务器,也有其处理能力的上限。当我们的脚本请求过多的数据时,TradingView不会持续地进行计算,而是会触发’script requests too many securities’的错误信息。
当我们用一个已经在图表上运行的脚本遇到这种情况时,会在图表上看到’cannot compile script’(无法编译脚本)的提示:
由于其他TradingView错误也使用相同的提示,这条信息本身对我们帮助不大。但在Pine编辑器的控制台窗口中,有一条信息更丰富的错误。
在那里,会显示类似以下内容:
Processing script...
Script requests too many securities: 48. The limit is 40
Script 'Error example' has been saved
接下来,让我们看看这个错误的含义以及如何修复我们的脚本。
在TradingView Pine中修复请求过多证券的错误
这里有好消息也有坏消息。好消息是,我们通常可以通过修改代码来修复’script requests too many securities’的错误。坏消息是,这个错误源于TradingView平台的硬性限制:即便是编写得完美无缺的脚本,也可能触发它。这意味着我们并不总能修复这个错误。
但以下是我们处理TradingView的’script requests too many securities’错误的方法。首先,检查代码中是否存在任何不必要的 request.security() 调用。是否有一些实际上相同、但没有额外价值的 request.security() 代码行?是否有一些你本想移除但仍然在运行的代码?或者你是否复制粘贴了一些尚未审查的代码?
如果在检查完错误后问题依旧,那么请审视每一个 request.security() 函数所请求的数据。为了进行优化,请关注该函数的第三个参数。一个能有效减少 request.security() 调用次数的技巧是:假设我们想获取每日的开盘价和收盘价之差。我们可以用两个 request.security() 调用来编写:request.security(syminfo.tickerid, "D", open) - request.security(syminfo.tickerid, "D", close)。但我们也可以将计算直接放入 request.security() 的第三个参数中,像这样:request.security(syminfo.tickerid, "D", open - close)。这能让我们得到相同的数据,但只用了一个数据请求。
如果脚本已经尽可能地减少了 request.security() 的调用次数,那么最后的办法就是将代码逻辑分散到多个脚本中。这对于指标脚本来说效果很好,因为你可以轻松地将多个指标添加到同一个图表中。(见下文示例。)
为了理解是哪些情况触发了’script requests too many securities’错误,让我们来看看什么代码算作一次证券数据请求,什么又不算。
在TradingView Pine中,什么是证券数据调用?
通过TradingView的 request.security() 函数,我们可以在脚本中加载其他交易品种和/或时间框架的价格数据。这使得我们可以用任何我们想要的品种和时间框架的数据进行计算,而不受当前图表的限制。
每次我们用 request.security() 请求数据时,TradingView都会为我们加载这些数据。由于这对TradingView的服务器来说是资源密集型操作,因此存在一个限制:单个脚本最多可以发出40个不同的 request.security() 数据请求。
四十个请求已经相当多了。但并非每一个 request.security() 的调用都会被计为一个请求。例如,思考以下这个示例脚本:
//@version=5
indicator(title="request.security() 示例 - 1")
valueA = request.security(syminfo.tickerid, timeframe.period, close)
valueB = request.security(syminfo.tickerid, timeframe.period, close)
plot(valueA, color=color.orange)
plot(valueB, color=color.teal)
这里,我们执行了两次 request.security()。但由于我们每次都使用了完全相同的参数,就TradingView而言,这并非两次不同的调用,而只算一次。当TradingView为第一个请求加载数据时,它已经拥有了第二个请求所需的数据。
TradingView做的另一个优化是移除未被使用的 request.security() 调用。例如:
//@version=5
indicator(title="request.security() 示例 - 2")
valueA = request.security(syminfo.tickerid, timeframe.period, close)
plot(valueA, color=color.orange)
request.security(syminfo.tickerid, "60", close) // 这个调用未被使用
plot(ta.ema(close, 20), color=color.teal)
这里我们有两个不同的 request.security() 调用,但实际的数据请求只有一次。这是因为第8行的调用结果没有在任何地方被使用。TradingView足够智能,不会去获取用不到的数据。
另一点是,每次我们执行 request.security() 时,我们就会发出一次数据请求。但这并不意味着代码中每一行 request.security() 都算一次调用。重要的是它被实际执行了多少次。例如:
//@version=5
indicator(title="request.security() 示例 - 3")
data(sym) => request.security(sym, timeframe.period, close)
plot(data("EURUSD"), color=color.orange)
plot(data("GBPUSD"), color=color.orange)
这里,代码中只有一次 request.security() 调用,但我们加载了两次数据。因为我们用不同的参数(”EURUSD” 和 “GBPUSD”)调用了 data() 函数两次,所以这会计为两次独立的数据请求。
你可以自己做的一个优化,是善用 request.security() 函数的第三个参数。该参数指定了我们希望函数返回的表达式。我们通常只传入一个简单的价格变量(如 high 或 close),但其实任何有效的表达式都可以。例如:
//@version=5
indicator(title="request.security() 示例 - 4")
dailyRange = request.security(syminfo.tickerid, "D", high - low)
plot(dailyRange, color=color.teal)
这里,request.security() 返回了该品种每日的最高价和最低价之差。但我们只通过一次调用就获取了这个数据。我们只是将 high - low 这个表达式直接传入,而不是分别调用两次来获取 high 和 low。
以下是其他一些可以传入 request.security() 的表达式示例:request.security(syminfo.tickerid, "D", ta.ema(close, 10)) 返回日线收盘价的10周期EMA;request.security(syminfo.tickerid, "D", ta.sma(close, 5) - ta.sma(close, 45)) 返回日线5周期和45周期SMA的差值;request.security(syminfo.tickerid, "D", close > open) 如果日线收阳,则返回 true。
现在,让我们来看一个会触发’script requests too many securities’错误的示例脚本。
错误示例:请求过多证券的指标
所以,我们不应在单个脚本中发出超过40次的数据请求。幸运的是,这个限制足够高,我们很少会遇到这个错误。但这里有一个不必要地复杂的脚本,它就会触发这个错误:
//@version=5
indicator(title="错误示例", overlay=true)
data(sym) =>
opens = request.security(sym, "D", open) + request.security(sym, "60", open)
highs = request.security(sym, "D", high) + request.security(sym, "60", high)
lows = request.security(sym, "D", low) + request.security(sym, "60", low)
closes = request.security(sym, "D", close) + request.security(sym, "60", close)
hl2s = request.security(sym, "D", hl2) + request.security(sym, "60", hl2)
hlc3s = request.security(sym, "D", hlc3) + request.security(sym, "60", hlc3)
(opens + highs + lows + closes + hl2s + hlc3s) / 12
plot(data("EURUSD"), color=color.orange, style=plot.style_circles)
plot(data("EURGBP"), color=color.orange, style=plot.style_circles)
plot(data("BTCUSD"), color=color.orange, style=plot.style_circles)
plot(data("AUDCAD"), color=color.orange, style=plot.style_circles)
这个脚本创建了一个 data() 函数,该函数从日线和60分钟线两个时间框架请求了6种不同的价格数据,总共进行了12次 request.security() 调用。
接着,我们用4个 plot() 语句,分别以4个不同的品种代码作为参数来调用 data() 函数。这总共导致了48次数据请求(4个品种 x 每个品种12次独立请求)。这显然超过了40次的限制,因此触发了’script requests too many securities’错误。
但我们仍有几种方法让这个脚本工作。一种选择是将代码拆分成两个不同的指标。这并不理想,但确实有效。例如,一个脚本处理EUR/USD和EUR/GBP:
//@version=5
indicator(title="错误示例 - 第1部分", overlay=true)
// ... (data函数代码同上) ...
plot(data("EURUSD"), color=color.orange, style=plot.style_circles)
plot(data("EURGBP"), color=color.orange, style=plot.style_circles)
然后在另一个脚本中我们绘制BTC/USD和AUD/CAD的数据:
//@version=5
indicator(title="错误示例 - 第2部分", overlay=true)
// ... (data函数代码同上) ...
plot(data("BTCUSD"), color=color.orange, style=plot.style_circles)
plot(data("AUDCAD"), color=color.orange, style=plot.style_circles)
另一个更好的解决方案是优化我们的代码。通过将多个价格的求和运算直接放入 request.security() 的表达式参数中,我们可以重写 data() 函数:
//@version=5
indicator(title="错误示例 - 调整后", overlay=true)
data(sym) =>
dayData = request.security(sym, "D", open + high + low + close + hl2 + hlc3)
hourData = request.security(sym, "60", open + high + low + close + hl2 + hlc3)
(dayData + hourData) / 12
plot(data("EURUSD"), color=color.orange, style=plot.style_circles)
plot(data("EURGBP"), color=color.orange, style=plot.style_circles)
plot(data("BTCUSD"), color=color.orange, style=plot.style_circles)
plot(data("AUDCAD"), color=color.orange, style=plot.style_circles)
这个调整后的版本仍然计算相同的数据,但我们将每次函数调用所需的数据请求从12次减少到了2次。这样,整个脚本的总请求数就从48次锐减到了8次。
总结
通过TradingView的 request.security() 函数,我们可以加载其他时间框架和/或交易品种的价格数据,从而在当前图表上进行跨品种、跨周期的计算。
然而,request.security() 的使用是有限制的:单个指标或策略脚本中,最多可以发出40次不同的数据请求。当我们超出这个限制时,TradingView便会生成’script requests too many securities’错误。
这个错误是平台的硬性限制,意味着我们不总能修复它,但可以设法绕过。首先,检查代码中是否有不必要的 request.security() 调用。其次,我们可以优化我们的调用,将计算表达式直接作为第三个参数传入 request.security(),例如 request.security(..., "D", high - low)。
如果优化后仍然超出限制,你将需要将代码逻辑分散到多个脚本中,以确保每个脚本的数据请求都少于40次。
TradingView的”循环太长”错误是怎么回事?
循环(loop)可以让我们重复执行某段代码,这使得编写更高级的脚本成为可能。但与此同时,它也带来了更多潜在的错误。’loop is too long’(循环过长)就是其中之一。让我们来看看这个错误到底意味着什么,以及我们该如何处理它。
剖析TradingView的’loop is too long’错误
通过循环,我们可以重复执行一段代码,并在每次迭代中使用略微不同的值。例如,我们可以用循环来计算过去20根K线的平均价格,或者找出过去10根K线中的最高点。循环往往能让编程变得更简单。
但循环中的一个小错误可能会对我们的脚本产生巨大影响。我们可能遇到的一个错误就是’loop is too long’。这个错误会直接显示在我们有问题的脚本所在的图表上,像这样:
与其它TradingView错误不同,’loop is too long’错误不会在Pine编辑器的控制台窗口中提供额外信息。不过幸运的是,即便没有更多信息,修复这个错误也并不困难。
修复TradingView的’loop too long’错误
当一个包含循环的脚本在单根K线上的计算时间过长时,就会出现’loop is too long’的错误提示。目前,TradingView对每个脚本在每根K线上的计算时间限制为100毫秒。如果脚本完成其计算所需的时间超过了这个限制,TradingView就会报错。
幸运的是,这个限制相当宽松。换句话说,TradingView脚本的计算速度已经非常快了。但如果你确实遇到了这个错误,可以尝试以下方法来解决它。
首先,检查你的循环代码是否存在明显错误。如果有,修正它并保存脚本,看看错误是否消失。你需要检查的地方包括:循环次数是否超出预期,比如意外地多输入了一个零;循环内部是否存在不必要或不正确的计算,例如计算了一个在循环中并未使用到的值,或者错误地计算了比所需更多的数据(比如本该计算10周期EMA,却写成了100周期);是否存在本可以用单个循环完成,却错误地使用了嵌套循环的情况,一个快速的检查方法是看循环的计数变量(如i、j)是否每一个都被有效地用来引用历史数据。
如果没有代码错误,那么你需要寻找优化代码的方法,以降低脚本的计算时间。这里有一些可以启发你的问题:循环内的代码是否可以被一个功能相同的TradingView内置函数所替代?如果是,请用内置函数替换它,因为内置函数通常运行得快得多。脚本中是否包含多个独立的循环?看看是否能将它们的逻辑合并到一个单一的循环中,这样每次脚本计算时,你只需要遍历一次历史K线。代码中是否存在嵌套循环?请仔细检查这是否真的有必要,由于内层循环会在外层循环的每一次迭代中都完整运行一遍,移除不必要的嵌套循环能极大地提升代码运行速度。能否将一些计算移到循环之外?例如,如果循环需要将某个值与当前K线的RSI进行比较,你可以在循环开始前就计算好RSI的值并存入一个变量,然后在循环内部直接使用这个变量,而不是在每次循环中都调用一次 ta.rsi()。循环的每一次迭代是否都需要完整地执行所有代码?如果不是,可以使用 continue 关键字来跳过当前迭代中不必要的计算,直接开始下一次迭代。在循环内部,是否有可能发生某种情况,让你希望立即停止整个循环?如果是,可以使用 break 关键字来终止循环。循环是否调用了某个自定义函数,而那个函数内部又包含一个循环?看看是否能重构代码以避免多层循环,因为循环嵌套会急剧增加脚本的计算时间。
如果既没有代码错误,也没有明显的优化空间,那么我们就需要进行更大幅度的代码修改。由于’loop is too long’错误本质上是TradingView平台的一个限制,有时我们唯一能做的就是减少循环内的代码量。如果可能,你可以尝试将代码逻辑分散到多个独立的脚本中。否则,你就必须降低你的预期,精简循环内的代码,使其运行得更快。
重要提示:TradingView的’loop is too long’错误指的是执行循环所需的时间,而不是循环内的代码行数。一个执行复杂计算的短循环,可能比一个每行只执行基本操作的长循环花费更长的时间。
现在,让我们通过一个例子来看看如何修复一个计算时间过长的循环。
错误示例:不必要的外层循环
在TradingView中,我们可以使用嵌套循环,即一个循环内部包含另一个循环。外面的通常称为外层循环,里面的则称为内层循环。
嵌套循环会导致循环次数的急剧增加。假设外层循环运行5次,内层循环运行20次。那么对于外层循环的每一次迭代,内层循环都会完整地运行20次。这意味着,在单根K线上,这两个循环总共会产生100次迭代。
当我们组合循环时,就很有可能遇到’loop is too long’的错误。下面的指标就是一个例子:
//@version=5
indicator(title="错误示例", overlay=true)
pricesCount = 0.0
for i = 1 to 10000
for j = 1 to 50
pricesCount := close[j] + pricesCount
avgPrice = pricesCount / 500000
plot(avgPrice, color=color.teal)
这里我们组合了两个循环。外层循环从1迭代到10,000次。内层循环则在每一次外层迭代中都从1迭代到50次。这在每根K线上都产生了巨量的循环次数,远超平台限制,因此触发了’loop is too long’的错误。
但问题在于:这个外层循环是完全多余的。它的循环变量 i 在循环体内根本没有被使用。而且,这个计算50周期均价的尝试也存在逻辑错误:avgPrice = pricesCount / 500000。
实际上,我们只需简单地移除那个多余的外层循环,不仅能让脚本运行得更快,更重要的是,它也修复了’loop is too long’的错误。修复后的代码如下:
//@version=5
indicator(title="错误示例", overlay=true)
pricesCount = 0.0
for j = 1 to 50
pricesCount := close[j] + pricesCount
avgPrice = pricesCount / 50
plot(avgPrice, color=color.teal)
总结
循环让我们能以高效的方式重复执行代码。大多数TradingView循环都能正常工作。但当我们执行复杂计算时,循环可能会花费过长的时间才能完成。目前,一个使用循环的脚本在每根K线上的计算时间应在100毫秒以内。如果超出这个时间,TradingView就会报出’loop is too long’的错误。
修复这个错误有三种途径。首先,检查循环代码是否存在增加了其运行时间的编码错误。如果没有错误,再看是否能对循环内的代码进行优化。如果这两个建议都无法解决问题,那么你就需要进行更大幅度的代码修改,甚至可能需要移除循环内的部分代码。








