OptiNod 学院
前视偏差与重绘——确认信号最早可知的时点
区分变动的实时值、高周期未确认值、回溯标注的位置与未来数据泄漏。验证的重点不是图表上箭头的位置,而是信号最早可被知道的时点。
把箭头所在的K线,与该箭头最早可被知道的时点,分开记录。
重绘是一个宽泛的说法,指同一个计算或标注在历史和实时中呈现不同的现象。进行中的K线随价格变化,RSI或均线跟着变动,也算在内。并非所有变化都是错误或欺骗。问题在于使用者所理解的入场规则,与当时实际能够获得的信息不一致。TradingView重绘定义
前视偏差在其中更进一步,指把判断当时无法得知的未来信息放进了对过去的计算。信号短暂出现又消失的现象、把几天后才确认的高点标在过去K线上的做法、提前使用未来最终价格的错误,三者需要区分。同一种处方不能解决所有这些问题。
因此,不能仅凭胜率高,或箭头恰好落在高点,就判断它正常或有误。要确认某个值在什么时候确定,以及这个值在什么时候被用于信号和订单。本文的示例是用来说明原理的条件,不是经过验证的盈利策略,也不是真实交易结果。

进行中K线的变化不等于未来信息
当前K线的最高价、最低价和现价,在K线收盘之前都可能变化。用这些值计算的交叉条件,也可能一度为真,随后又变回假。仅因为当时读取了现价,并不构成未来泄漏。不过,事后留下的OHLC无法完全还原这些中间状态。
指标通常在每次实时价格更新时重新计算。策略默认在K线收盘时计算,使用 calc_on_every_tick 选项后,实时计算方式会改变。不能把这个策略选项说成所有指标共有的设置。也不要假定仅凭收盘价数据就能原样重现按实时逐笔计算的策略的行为。执行与K线状态
需要收盘确认的信号时,检查当前图表的 barstate.isconfirmed 条件。但它只是确认该图表K线已经收盘,并不会让其他周期的未确认值变成已确认,也不会修正回溯标注的位置。有意使用K线中途信号的做法也可以,但需要另行建立实时记录和验证标准。
lookahead设置要连同所引用的周期和表达式一起看
在低周期图表上,把尚未收盘的高周期K线的最终值放到过去较早的K线上,就会产生未来泄漏。例如,一周结束后才能知道的该周最终最高价,被提前用在周一的判断上。即便不附日期或具体资产价格,也能依据信息在什么时候存在来判定这个错误。
不能只搜索 lookahead_on 这个名称,就把找到的所有调用都判为错误。不带偏移地请求高周期的当前表达式,与请求已经结束的高周期K线,是两回事。同周期或低周期的请求也要单独审视。其他周期数据与lookahead
反过来,使用默认值 lookahead_off 也不代表高周期的值总是已确认。过去K线上的放置位置,与实时中进行中的HTF值可能不同。要确认刷新前后是否一致,需要看请求的周期以及该值被确认的时点。

已确认的HTF值要把前一根K线与放置选项作为一对来处理
只想使用已确认的高周期数据时,官方文档说明的做法是把请求表达式的前一根K线值 expression[1] 与 barmerge.lookahead_on 配合使用。[1] 应当作用于 request.security() 所计算的高周期表达式。这与事后把返回的低周期序列再延后一根K线并不相同。已确认HTF的请求方式
本文讨论的方式仅限于请求周期严格高于图表周期的情形。相同周期或更低周期的输入应另行处理或拒绝。由于使用的是上一根HTF K线,代价是不会对进行中的HTF K线的最新走势作出反应。这并不保证改善业绩,也不保证消除所有类型的重绘。
仅仅把 barstate.isconfirmed 加到图表入场条件上,并不能让HTF值变成已确认。把它放进 request.security() 内部也无法替代。图表K线与请求K线是否已确认,要分别设计。barstate.isconfirmed的适用限制
回头画出的枢轴,不是在那根K线上可以知道的信号
以右侧需要两根K线的枢轴为例,候选K线之后出现两根K线、条件得到确认的时点,与枢轴候选K线本身的时点并不相同。把标记前移两根K线来画,形状更容易理解,但并不意味着那时已经可以入场。使用枢轴的订单,应在确认时点之后再评估。
以ZigZag的最后一段,或最近的波段为基准而移动的自动回撤线,在确认之前同样可能变化。确认规则因实现而异。要说某个资产的高点几天后才确认,需要当时的设置和实际记录。不能仅凭工具名称或高胜率就断定存在未来泄漏。
负的绘图偏移是把图形位置移向过去的功能,与读取未来数据的运算不同。Pine的历史引用运算符是用来引用前面K线的工具。不能把无效的负数历史引用,当作正常读取未来数据的语法来举例。检查源码时,要分别核对显示位置,以及计算所用信息的时刻。历史引用运算符

警报与订单的时点要与图表标注分开确认
如果是基于K线收盘的规则,警报也要按该规则设置。不过,alert() 调用的频率、由 alertcondition() 创建的警报的设置、策略订单成交警报,三者各不相同。不要假定“每根K线收盘一次”的设置统一适用于所有警报。同时确认正在运行的警报所使用的数据源和输入值。TradingView警报文档
即使警报按当前图表的收盘设置,只要条件是用进行中的HTF值生成的,之后也可能发生变化。警报频率设置不能取代数据确认规则。警报已发送这一事实,也要与订单确实已被接收、成交的证据区分开。
订单似乎在信号确认那根K线的收盘价成交,不能立即判为前视。先确认策略的成交选项和订单类型。模拟成交方式是否反映了真实的传递延迟和可成交价格,在回测与实盘对比中另行核查。
刷新前后的比较需要相同的数据条件
历史K线也可能因数据供应商的修正、股票拆分等调整,以及加载的历史起点变化而不同。如果抱着“已收盘的K线永远不会改变”的前提,只怀疑代码,就会忽略真正的原因。要在相同的源码版本、输入值、交易所、合约、普通K线、周期、交易时段、数据范围下进行比较。数据变化引起的重绘
确认时点验证条件 — 选择普通5分钟K线和1小时HTF,观察1小时边界前后的实际更新。开始把信号用于入场判断的时点,是在通过事先设定的确认条件之后。这个测试不下订单。如果无法收集实时记录,或源码、设置、数据发生变化,就中止比较,并把按原因划分的验证标记为无效。
实时观察期间,记录时刻与时区、图表K线时刻、HTF区间、数值与信号、最初的显示位置。K线收盘后核对同一位置,再与重新加载后的结果对照。屏幕录像、截图与警报日志可以互相补充,但没有警报的盘中状态,不能只靠警报日志还原。

以可确认的行为而非胜率作为检查标准
- 已区分实时的变动值、图表K线确认值、HTF确认值。
- 已一并审视HTF请求的表达式、偏移、lookahead与周期限制。
- 已分别记录枢轴和ZigZag的显示K线与实际确认K线。
- 已区分警报类型、频率、正在运行的设置与订单成交记录。
- 已把固定源码和数据条件的实时观察记录,与刷新后的结果作了对比。
Bar Replay是观察历史序列和显示延迟的辅助工具。Strategy Tester CSV是该次模拟的交易记录。仅凭这两者,无法证明当时的所有实时逐笔数据和信号。尚未实际完成TradingView编译、图表运行和实时观察的示例,不要当作已验证的代码。与其把“non-repainting”这一说法或某个胜率作为通过标准,不如留下记录,说明信号是在什么时点、依据什么信息产生的。