透過窺視未來,策略產生不切實際的良好結果

開發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])//@version=4strategy("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])//@version=4strategy("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])//@version=4strategy("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])//@version=4strategy("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])//@version=4strategy("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])//@version=4strategy("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])//@version=4strategy("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])//@version=4strategy("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])//@version=4strategy("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 more    if 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()獲取數據,而不會發生前瞻性風險。

目前,這些都是策略展望未來的所有已知方法。我們希望此描述將使您能夠建立沒有這些缺點的策略,並避免已發表策略的作者在其想法中利用這些功能。