通过窥视未来,策略产生不切实际的良好结果

开发Pine语言的主要目标之一是为用户提供尽可能多的有用工具。这些工具可能具有各种不同的用途,并且通过某些操作,某些指标和图表类型使您可以从未来的K线或交易(相对于当前处理的K线)中提取数据。由于交易者无法在实际交易中接收此数据,因此围绕该数据构建的策略在回测时可能会产生不切实际的获利结果,而在实时交易中,这些交易会造成损失。在策略中使用未来信息的错误也被称为前瞻性偏见。

 

一些TradingView用户出于无知或出于恶意,倾向于利用此功能来创建观点和脚本发布。TradingView无法删除功能本身,因为它在某些情况下可能有用,但是同时,我们致力于警告用户这种行为。

 

使用日式K线的策略

 

这种行为的一个很常见的原因是对日式图表(Renko,Kagi等)进行策略回测。问题来自于策略回测引擎将每个K线视为4笔交易,其价格为开盘价,最高价,最低价和收盘价(常规K线图就是这种情况)。因此,在Renko图表上,策略回测引擎可以用实际上不存在的价格进入/退出仓位。此外,如果您将箱子尺寸(Box Size)的值设置为小于mintick,则可以在回测引擎处理实际价格之前,检查下一个价格是否高于或低于当前价格,并提前进入/退出仓位。

//@version=4
strategy("My Strategy", overlay=true)
if close < close[1]    strategy.entry("ShortEntryId", strategy.short)
strategy.close("ShortEntryId", when = close > close[1])if close > close[1]    strategy.entry("LongEntryId", strategy.long)
strategy.close("LongEntryId", when = close < close[1])
JavaScript

如您在屏幕截图中所见,这种简单的策略可以使交易价格非常接近最大/最小交易价格。

 

使用参数calc_on_order_fills = true的策略

 

当在策略函数中指定了参数calc_on_order_fills = true时,回测引擎将在执行订单后在K线内执行额外的计算(与通常仅在K线收盘时计算策略的通常情况相反)。同时,在计算过程中,该策略可以访问许多其他的K线参数,例如最高值和最低值。这使您可以编写一个在回测时显示出色表现的策略:


//@version=4
strategy("CalcOnOrderFillsStrategy", overlay=true, calc_on_order_fills=true)// a variable is used to prevent double entry on the same bar var lastTimeEntry = 0 longCondition = close > sma(close, 14) and lastTimeEntry != time if longCondition strategy.entry("LongEntryId", strategy.long) strategy.exit("exitId", "LongEntryId", limit=high) lastTimeEntry := time
JavaScript

在屏幕截图上,您会看到进场点是在一根K线的开盘价处,而退出点是在同一根K线的最高价发生。也就是说,在计算过程中,执行订单后,我们将strategy.exit极限价格设置为等于当前K线的高位,这在实际交易中是无法做到的。

 

安全性以及Pine v3之前的任何安全性中的lookahead = barmerge.lookahead_on参数

 

Pine中的安全功能允许您从其他交易品种和/或时间周期中请求数据。根据执行方式的不同,这可能使该策略可以接收来自未来的数据:例如,如果有请求日K线的收盘价或最高价,当回测该策略时,则可以在一天的开盘时就知道这些值。

在v3之前,安全功能将在较高的时间周期内返回该值,甚至在该值应已可访问之前也是如此。在v3中,此行为已修复,但是出于兼容性考虑,向安全功能添加了lookahead参数。默认情况下为false(即关闭了未来视角( future vision)),但是您可以通过将参数lookahead的值设置为barmerge.lookahead_on来启用它。

 

使用此功能构建的获利策略的示例:

//@version=4
strategy("My Strategy", overlay=true)
dayStart = security(syminfo.tickerid, "1D", time, lookahead=barmerge.lookahead_on)
dayHigh = security(syminfo.tickerid, "1D", high, lookahead=barmerge.lookahead_on)
dayLow = security(syminfo.tickerid, "1D", low, lookahead=barmerge.lookahead_on)// entry at first bar of a day
if time == dayStart    // distance to daily high is further, so we can earn moreif abs(open - dayHigh) > abs(open - dayLow)        strategy.entry("LongEntryId", strategy.long)        strategy.exit("exitLongId", "LongEntryId", limit=dayHigh)else        strategy.entry("ShortEntryId", strategy.short)        strategy.exit("exitShortId", "ShortEntryId", limit=dayLow)plot(dayHigh)
plot(dayLow)
JavaScript

在第一根K线上,我们分析价格相对于开盘价将上涨还是下跌,并以此为基础,输入多头或空头仓位,然后分别以当天的最高价或最低价退出。

 

请注意,并非所有security()具有barmerge.lookahead_on参数的情况都面向未来:例如,如果我们要通过将security()内部的time/high/low分别替换为time[1]/high[1]/low[1] 来更改上面的代码, 我们将接收已经收盘的K线的值。有经验的编码人员经常使用它来从security()获取数据,而不会发生前瞻性风险。

 

目前,这些都是策略展望未来的所有已知方法。我们希望此描述将使您能够创建没有这些缺点的策略,并避免已发表策略的作者在其观点中利用这些功能。