OptiNod 学院

回测与实盘:找出收益曲线分叉的第一笔交易

先对齐代码、市场和成交设置,再核对信号、订单、成交和成本记录。本文介绍在不预先断定曲线差异原因的前提下,逐项验证的顺序。

收益曲线出现分叉时,先确认比较的是否为同一组条件。然后再找出第一个不同的信号和成交。


回测是用固定的数据和执行规则重建交易得到的结果。即使使用相同的源码和输入值,只要数据范围、交易市场或订单设置不同,就可能产生不同的交易。仅凭与实盘曲线存在差异,不能判断策略没有优势,也不能判断成交系统出了故障。


最先要比较的不是收益率,而是条件。需要固定的有:源文件和版本、输入值、交易所和标的、现货或永续合约等合约类型、普通K线、周期、交易时段和日期范围、初始本金和下单数量、手续费与滑点设置。要区分图表显示的时区和交易所的交易时段,并记录数据起点和预热区间。


这些基准对齐之后,才能按时间顺序核对信号产生、订单生成、交易所受理、实际成交和成本计入。如果把“一定以K线收盘价成交”的假设套用到所有回测上,反而会漏掉原因。从成交时点到数据偏差,需要分开检查五个不同的环节。


逐笔查看回测曲线与实盘曲线差异的概念图
逐笔查看回测曲线与实盘曲线差异的概念图

同一个信号,订单生成与成交时点也可能不同


TradingView策略的默认计算在K线收盘时进行,新的市价单默认并不会在那一刻立即成交。经纪商模拟器原则上在下一个可用的tick成交,在历史K线上通常对应下一根K线的开盘价。process_orders_on_close 之类的选项会改变这一行为。收盘价成交选项本身不能直接称为未来函数,但警报的传递以及实际订单能否按该价格成交,是另外的问题。TradingView策略文档


因此,入场价不同时,先对齐信号所在的K线和订单生成时刻。然后依次确认回测的成交规则、订单类型、警报接收时刻、交易所受理时刻和平均成交价。跳空、交易中断、订单被拒、数量取整、选择了不同的合约,也会造成差异。不能仅凭一个价格差就断定是延迟或滑点。


用于比较的验证设置 — 在同一个普通K线15分钟周期上,只比较收盘确认的信号,市价单按默认模拟器的成交方式记录。入场取该信号之后第一个可能的成交,止损固定在预先确定的信号K线最低价之下的价格。这是用于记录比较的条件,不是实际交易建议。信号时刻、数量、止损规则中只要有一项不同,就把该笔交易从成交质量比较中剔除。


查看信号确认与订单成交之间时间差的概念图
查看信号确认与订单成交之间时间差的概念图

滑点要同时记录基准价格和订单大小才能解读


滑点是所选基准价格与实际平均成交价之间的差。以信号价格、下单前的中间价、最优买价和卖价中的哪一个为基准,数字会不同。市价买单吃掉多档卖盘时,平均成交价可能比最初看到的最优卖价更不利。反过来,订单到达之前价格向有利方向变动的情况也存在,所以不要假定每笔交易的差额都是损失。


不要把固定的几个bp当作所有市场的标准答案。应按交易所、合约、时间段和下单金额收集实际样本,并把平常时段与流动性下降的时段分开来看。如果没有盘口记录,市场冲击和通信延迟各自的影响就难以区分,这一局限也要保留。限价单可以限制价格,但需要同时考虑未成交和部分成交的可能。


验证时,把观测到的成本作为基准情景,把更不利的成本作为压力情景。同时检查成本提高后,留下来的交易是否发生变化。固定滑点选项是敏感度检查工具,并不能重现真实的盘口。


手续费按成交额计算,资金费率按持仓条件计算


手续费要确认交易所对应的产品、账户等级、Maker和Taker条件,再按每笔成交的成交额计算。如果入场和出场的成交额不同,或存在部分成交,结果会与一次简单的往返费率不同。手续费资产的折算时点也要与实际明细一致。


举例来说,按说明用的假设,买入和卖出的成交额各为1,000美元,每次成交手续费为0.05%,则往返成本为1美元。同样的交易做100次,就是100美元。这占账户的百分之几,取决于本金和数量。不能把交易次数乘以往返费率的结果直接从账户收益率中扣除。


永续合约的资金费率可能支付,也可能收取,取决于各产品的结算周期、当时的费率、持仓方向和结算对应的数量。并非所有产品都每8小时按相同比例扣除。应使用对应交易所的结算记录和最新的产品规则,并把资金费率与手续费作为不同项目分别核对。适用条件也可以在交易成本说明中一并检查。


查看手续费如何随成交额和次数累积的概念图
查看手续费如何随成交额和次数累积的概念图

同一根K线内的止损和止盈,只凭四个价格无法确定先后


普通OHLC K线显示开盘价、最高价、最低价和收盘价,但不保存其中所有价格的先后顺序。如果同一根K线同时触及止损价和止盈价,哪个订单可能先成交就会改变结果。实际上,还需要确认订单被激活的时点和部分成交。


设想一个假设示例:做多仓位,入场价为100,止损价为98,止盈价为103。仅凭该K线最高价为104、最低价为97这一信息,无法知道哪一个出场先发生。TradingView的默认经纪商模拟器使用既定的K线内部路径假设,因此说它总是先处理止损是不对的。Bar Magnifier利用可用的更小周期数据来改进路径估计。模拟器与Bar Magnifier


更小周期的数据也无法证明盘口队列、全部tick以及真实账户的成交。请标出存在冲突的交易,再确认更细的数据和订单记录。没有数据的区间只能保留为估计。Heikin Ashi或Renko等非标准图表可能包含合成价格,所以先用普通K线对齐比较基准。非标准图表数据


同一根K线同时触及止损和止盈时需要确认先后顺序的概念图
同一根K线同时触及止损和止盈时需要确认先后顺序的概念图

标的选择和信息的可获得时点也是验证对象


如果只用现在存活的标的来构成过去的投资对象,就与当时真实可选的样本不同。要确认是否已反映退市、停牌、新上市以及过去的流动性标准。存在幸存者偏差并不意味着所有策略都能盈利,但遗漏了亏损标的的结果,不能推广到整个市场。


未来函数,是指把判断当时无法得知的信息放进过去的决策中。使用正在形成的K线的当前价格,本身并不是看到了未来。问题在于,把之后才确认的值当作更早时点就已知道,或者把用整个验证期算出的统计量用在过去的学习过程中。更高周期的确认值以及枢轴点的确认时点,在未来函数与重绘说明中另行确认。


数据提供方对价格的修正,或计算起点的变化,也会改变重新运行的结果。除了源码和输入值,还要同时保存原始数据范围和提取时间,才能回到同样的条件。TradingView数据变化说明


区分当时可选标的与现在留存标的的概念图
区分当时可选标的与现在留存标的的概念图

不要给一个症状只贴一个原因


下表不是用来确定原因的,而是用来挑选下一步要确认的对象的顺序。从第一个出现差异的交易开始调查,可以减少因之后余额和数量不同而产生的派生差异。


观察到的症状可能的原因需要确认的测试与证据判断的局限
信号所在K线或方向不同代码、输入、市场、数据不同,未确认的HTF,实时计算固定版本和设置,把当时的信号记录与重新运行的结果对照仅凭重新运行的CSV无法还原当时的实时信号
信号相同但入场价不同成交选项、延迟、盘口与订单大小、订单类型比较订单生成、接收、受理、成交的时刻以及当时的盘口没有盘口和时钟同步,就难以分离各原因的贡献
只有实盘缺少交易警报遗漏、被拒、最小数量、未成交、持仓限制关联警报ID和订单ID、拒绝代码、数量和余额记录警报发出或API受理成功,并不能证明已成交
同一根K线的出场结果相反K线内部路径、订单激活时刻、跳空、部分成交对照更小周期的路径与实际成交明细更短的K线也不能完整证明tick路径和盘口队列
交易相近但净损益不同手续费、资金费率、折算、数量、损益口径不同按每笔成交的本金和成本,以同一币种重新计算仅凭交易频率不能确定账户的成本比例
刷新后过去的信号改变未确认的值、对过去位置的标注、数据修正、历史范围变化对照前后的画面、数据、代码以及最初发现的时刻仅凭变化不能断定是故意为之或存在未来函数

留下记录,回测差异才能用于下一次验证


在开始实盘或模拟运行之前,先确定比较条件和确认期间。期间要按交易数量和策略周期来定,不要因为结果看起来好就中途更改标准。保存信号ID、K线的开始和收盘时刻、信号确认时刻、警报和订单ID、请求数量、成交价和成本,就能把同一笔交易一直串联到底。


  • 已对齐代码版本、输入值、交易所、合约、普通K线、周期、交易时段和日期范围。
  • 已记录计算时点、订单生成与成交选项、本金与数量规则。
  • 手续费和资金费率按实际适用条件,滑点按有依据的范围进行了比较。
  • 已标出止损与止盈的冲突、数据空缺以及当时样本的局限。
  • 已用同一时间基准串联首个不一致交易的信号、订单、成交和成本。

Strategy Tester的CSV是该次模拟的交易记录。Bar Replay也只是查看历史数据的工具。两者都不能单独证明过去确实存在实时信号。除了重新运行的结果,还需要当时的观察记录。如果仍有无法解释的差异,在对收益曲线增加信心之前,先补全该笔交易的证据。

检查你自己的回测结果

上传优化结果 CSV 或 TradingView 策略的交易列表,即可同时查看绩效指标和稳健性检查结果。无需登录即可分析。

分析我的结果文件 查看分析示例