You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pine Script security策略回测消除1bar延迟的合规实现方法咨询

Pine Script security函数跨时间帧调用在策略回测中出现1根K线延迟问题及解决方案

问题背景

我多次阅读了security() revisited [PineCoders]和How to avoid repainting when using security() - PineCoders FAQ两篇文档后,仍对如何正确将security函数返回的元组数据与当前图表时间帧的数据结合使用存在疑问。
经PineCoders团队推荐,我向资深开发者请教后拿到了如下处理元组的security封装函数及规则:

f_secWrapper2Tuple( _symbol, _resolution, _data1, _data2 ) =>
          security( _symbol, _resolution,
          [ _data1[1], _data2[1] ], 
          lookahead = true )
  • 正常调用指标函数获取无延迟的原始数据,比如基础RSI计算结果
  • 将原始数据直接传入security函数,由security自动施加1根K线的延迟
  • 绘图或使用当前图表时间帧的非security原始数据判定买入信号前,手动加1根K线延迟;security返回的数据已内置延迟,无需额外处理
  • 由于买入条件使用的前K线数据永远不会重绘,因此使用once per bar模式在K线开盘时生成警报,不要使用once per bar close模式。

现有方案表现

指标脚本(study)表现符合预期

我对所有原始数据都施加1根K线延迟,无论是否与security函数的数据共同构成买入信号。这套方案在指标脚本中生成警报的效果很好,调用更高时间帧(HTF)数据时无任何延迟:如果脚本运行在1小时低时间帧(LTF),结合日线HTF数据时,HTF条件在当日K线开盘时判定为真(通过1K延迟校验前一根已收盘HTF K线),LTF在当日00:00就会同步触发信号,不会额外延迟1根K线。
这套方案在指标脚本中生成警报的表现非常理想,只要K线开盘时条件满足,警报就会触发。对应截图如下:
Study script

策略脚本(strategy)回测存在1K延迟

但我在策略脚本中回测这套逻辑时,会产生1根LTF K线的延迟,相同的买入信号直到1小时后的19:00才会触发:
Strategy script
我单独测试了仅使用当前时间帧信号(无security调用)的场景,对比不读取前K线数据、使用once per bar close生成警报的脚本,发现1K延迟会让2020年1月1日起的回测净利润从约3000%下降到约2000%。我尝试开启calc_on_every_tick和calc_on_order_fills参数,也没有解决问题。

我有三个核心疑问:

  1. 回测的用法是否存在错误?
  2. 如果用法正确,有没有可行的 workaround 可以让回测时买入订单在正确的K线开盘时成交?
  3. 是否可以在回测时移除security和当前时间帧数据的1K延迟,或者使用其他security函数写法,让回测性能结果与实盘运行的指标脚本(施加1K延迟、使用once per bar警报)的预期表现一致?我在指标脚本中使用security的方式是否也适用于策略回测?

补充示例代码

指标版本(运行正常)

//@version=4
study( "SMA Security Demo (study)", overlay = true )

i_useSecurity = input ( true, "Use security", type = input.bool )
i_symbol = input( title="Symbol", type = input.symbol, defval = "")
i_resolution = input( title="Resolution", type = input.resolution, defval = "" )

// 1 is what I use for study scripts to send alerts on once per bar (open)
i_realtimeOffset = input( title = "Reatime Offset", defval = 1 , options = [ 1, 0 ] )
// 1 is what I use as 0 means using repainting realtime bar data and I am not trying to do this
i_securityOffset = input( title = "Reatime Offset", defval = 1 , options = [ 1, 0 ] )

f_secWrapper( _symbol, _resolution, _data1 ) =>
          security( _symbol, _resolution,
          _data1[i_securityOffset], 
          lookahead = true )

sma50Raw = sma( close, 50 )
sma100Raw = sma( close, 100 )

smaBuyRaw = sma50Raw > sma100Raw

smaBuySec = f_secWrapper( syminfo.tickerid, "240", smaBuyRaw )

buy = smaBuyRaw[i_realtimeOffset] and ( i_useSecurity ? smaBuySec : true )

plot( sma50Raw, color = color.yellow )
plot( sma100Raw, color = color.blue )

bgcolor( buy ? color.green : color.red )

上方指标脚本运行在1小时周期的上层图表中,当本周期黄色SMA50上穿蓝色SMA100后,且开启use security开关时,会同步校验4小时周期的相同条件,满足后触发买入信号。我使用的实时偏移参数为1,背景从红转绿即代表买入信号触发,由于始终引用前一根已收盘K线的数据,在当前K线开盘时入场没有repainting风险,警报使用once per bar而非once per bar close模式生成。下层的4小时周期图表仅运行了同条件的背景变色脚本作为参考。
Study version, dual chart
从上图可以看到,读取4小时HTF数据没有任何延迟:当UTC时间18:00新的1小时和4小时K线同时开盘时,两个周期的条件首次同时满足(上次卖出信号后)。原因是1小时周期的前K线已经满足SMA50大于SMA100的条件,而4小时周期的前K线刚好在收盘时首次满足该条件。

策略版本(存在1K延迟)

//@version=4
strategy("SMA Security Demo (strategy)", overlay=true, margin_long=100, margin_short=100)

i_useSecurity = input ( true, "Use security", type = input.bool )
i_symbol = input( title="Symbol", type = input.symbol, defval = "")
i_resolution = input( title="Resolution", type = input.resolution, defval = "" )

// 1 is what I use for study scripts to send alerts on once per bar (open)
i_realtimeOffset = input( title = "Reatime Offset", defval = 1 , options = [ 1, 0 ] )
// 1 is what I use as 0 means using repainting realtime bar data and I am not trying to do this
i_securityOffset = input( title = "Security Offset", defval = 1 , options = [ 1, 0 ] )

f_secWrapper( _symbol, _resolution, _data1 ) =>
          security( _symbol, _resolution,
          _data1[i_securityOffset], 
          lookahead = true )

sma50Raw = sma( close, 50 )
sma100Raw = sma( close, 100 )

smaBuyRaw = sma50Raw > sma100Raw 

smaBuySec = f_secWrapper( i_symbol, i_resolution, smaBuyRaw )

buy = smaBuyRaw[i_realtimeOffset] and ( i_useSecurity ? smaBuySec : true )

plot( sma50Raw, color = color.yellow )
plot( sma100Raw, color = color.blue )

bgcolor( buy ? color.new( color.lime, 75 ) : color.new( color.red, 75) )

if buy
    strategy.entry("buy", strategy.long )
else
    strategy.entry("sell", strategy.short )

使用相同代码时会出现1根K线的延迟,如下图所示:
Strategy version, dual chart
我在代码中增加了偏移参数选项,不仅支持调整实时数据偏移,也支持调整security数据偏移——尽管后者会用到重绘数据,这不是我想要的效果。
将实时偏移设为0不会改变策略订单的触发位置,订单仍在UTC 18:00才触发。如果将security偏移设为0,由于用到了重绘数据,条件会提前4小时在UTC 08:00就判定为真,但策略仍然延迟到下一根K线才下单。
Strategy version, dual chart

我希望找到支持元组的security函数写法,在指标脚本中实现无延迟不重绘的HTF数据调用,同时不需要对LTF/当前图表数据施加延迟,还能支持once per bar close警报和正常回测。


问题原因与解决方案

延迟的核心原因

TradingView策略默认的订单成交规则是:当K线运行过程中条件触发时,默认会在下一根K线的开盘价成交,避免用到未来数据;而指标的bgcolor是在当前K线开盘就可以根据历史数据渲染,两者的触发逻辑本身就存在时序差。你使用的lookahead=true参数在指标端可以正确读取前序HTF K线的已收盘数据,但在策略回测时,系统会自动规避前瞻逻辑,额外多推了一根K线的成交时机。

可行修复方案

  1. 调整策略的成交时机参数:在strategy声明中增加process_orders_on_close=true参数,同时把i_realtimeOffset设为0,i_securityOffset保持为1。这个参数会让策略在当前K线收盘时就判定条件并提交订单,成交价格用下一根K线的开盘价,刚好匹配你实盘开盘触发警报入场的逻辑,不会产生额外延迟。
  2. security函数的写法不需要改动,原来带lookahead=true且传入偏移1的写法本身不会产生重绘,在调整策略参数后可以直接复用,同时支持元组返回值。
  3. 不需要修改alert的触发逻辑,实盘仍然用once per bar模式在开盘触发警报,回测结果和实盘表现可以完全对齐。

修改后的策略核心代码示例

// 修改strategy声明,添加process_orders_on_close=true
strategy("SMA Security Demo (strategy)", overlay=true, margin_long=100, margin_short=100, process_orders_on_close=true)

// 其他代码不变,调整输入参数默认值即可
i_realtimeOffset = input( title = "Reatime Offset", defval = 0 , options = [ 1, 0 ] )
i_securityOffset = input( title = "Security Offset", defval = 1 , options = [ 1, 0 ] )

内容的提问来源于stack exchange,提问作者saltyeggs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 02:42:00