跳到正文

为什么 TradingView 比你的回测工具多一笔交易

约 6 分钟阅读

多出来的是最后一笔:数据结束时仍未平仓的仓位,被 TradingView 按最后一根 K 线的收盘价记成了一笔已平仓交易。本文说明这一行是什么、为什么会改变你的统计数据,以及 PineForge 如何记录它。

同一个策略,TradingView 上的交易比你的回测工具多出一笔。多出来的是列表里的最后一笔:出场信号写着 Open,或者干脆是空的;出场价就是最后一根 K 线的收盘价。

两边都没有出错。数据走到尽头时还没平掉的仓位,TradingView 就是用这样一行来报告的。回测工具如果漏掉这一行,交易笔数、净利润,以及所有基于它们算出来的统计数据,都会和 TradingView 对不上。

TradingView 记下了什么

回测在最后一根 K 线处结束,策略并不会在那里自动平仓。TradingView 的报告不会丢掉这个未平仓的仓位,而是把它记成一笔已平仓交易:出场点是区间的最后一根 K 线,出场价是这根 K 线的收盘价,然后像其他交易一样计入统计。PineForge 模拟了这条规则,引擎的头文件里有说明。

公开语料库里就有一个例子。stats-eventrades-zero-pnl-count-01 在 UTC 零点那根 K 线上买入一个单位,以下一根 K 线的开盘价成交,再挂一张价格等于入场价的限价单卖出。它的最后一笔多单,价格再也没有回到入场价。下面是 TradingView 的交易列表(时间用的是 Asia/Taipei 时区,所以 08:00 就是 UTC 零点):

Trade number,Type,Date and time,Signal,Price USDT,...,Net PnL USDT,...
140,Exit long,2026-05-01 08:00,,2261.44,...,-2525.12,...
140,Entry long,2025-08-25 08:15,even-trade entry,4786.56,...,-2525.12,...

看出场那一行:没有信号,价格就是最后一根 K 线的收盘价。策略其实从来没有卖出过。这 −2,525.12 其实是持仓八个月的浮亏,却像已经实现了一样,出现在交易列表和净利润里。

这一行有两种写法。TradingView 自带的交易列表 CSV 导出,会在 Signal 列里写上 Open(这个策略较早的一份导出就是这样)。TradingView 的深度回测报告把这一栏留空,如今语料库里大多数文件也一样,就像上面那样(评分器)。

一行为什么要紧

对交易不频繁的策略或较短的回测区间来说,这个未平仓的仓位可能占了结果的大头。在日线图上,它常常是窗口内唯一的一笔交易:在 PineForge 开始记录这一行之前,它在这类策略上报告的是一笔交易都没有,而 TradingView 报告的是一笔。凡是从交易列表算出来的数字都会跟着变:交易笔数、胜率、净利润、平均每笔交易盈亏。

PineForge 怎么记

引擎过去只导出已平仓的交易。现在它也会记下 TradingView 记的这一行:最后一根 K 线之后仍未平仓的每一笔持仓各记一行,日期就是这根 K 线,价格是它的收盘价按最小变动价位取整,计手续费但不计滑点,因为这一行「是一个标记,不是一笔订单」(引擎指南)。它的交易列表在最后一列里标出了这一行:

Trade #,Type,Date and time,Price,Qty,Net PnL,...,Engine range-end
140,Exit long,2026-05-01 00:00,2261.440000,1,-2525.120000,...,open

K 线、价格、亏损都和 TradingView 一样,open 则表明它是一个标记。脚本本身看不到这一行:在运行过程中,这个仓位仍然没有平仓,所以 Pine 的 strategy.closedtrades 不会把它算进去;引擎文档也说明,如果要和这个变量对上,就把这一行过滤掉(Pine 到原生 API 对照表)。

数据截止在哪,价格就定在哪

这个标记记在回测工具的最后一根 K 线上,所以这根 K 线必须正是 TradingView 的最后一根。TradingView 的区间,止于在结束日期 UTC 零点或之前开盘的最后一根 K 线(harness)。语料库的行情文件比这更长,一直到 5 月 4 日。如果把整个文件喂给引擎,它会把第 140 笔交易记在那天的最后一根 K 线上,价格是 2,365.09,比 TradingView 的 2,261.44 高出约 4.6%(同一文件)。所以 harness 会在运行前,把数据截断在 TradingView 的最后一根 K 线处。

之后,引擎的评分器会把这两行当作普通交易来配对,它们的出场价和 P&L 也和其他交易一样参与评级。只有当 TradingView 把这一行写成 Open 时,评分器才把这一对当作「标记对标记」来处理:照常匹配和计数,检查入场,但出场价和 P&L 不计入评级,P&L 的吻合情况则精确到分报告出来(评分器)。

29 个策略,多出一笔交易

我们用当前的引擎重新生成语料库的交易列表后,312 个策略中有 29 个的交易笔数变了。其中 26 个新增的交易里,有一笔就是这一行。另外三个多出的是一笔已平仓交易,原因是 TradingView 自己的列表已经更新到了当天结束。

stats-eventrades-zero-pnl-count-01 的旧列表,当时照样拿到了评分器的最高评级。评分器会裁掉两份列表共同覆盖的窗口两端的交易,这样开头或结尾多出一笔,不至于让一个策略不及格。可在这里,这种裁剪恰好掩盖了缺失的那一笔:旧报告每边评了 139 笔交易,而 TradingView 列出的是 140 笔;新报告则是 140 笔对 140 笔。

在语料库的 312 个策略中,有 131 个策略的 PineForge 交易列表以一个未平仓的仓位收尾,超过四成(语料库 README)。

这对你意味着什么

  • 如果 TradingView 比你的回测工具多一笔交易,而且多出来的是最后一笔,先看看末尾是不是有个仓位还没平。这是 TradingView 的正常行为。
  • TradingView 的净利润和胜率,已经按最后的收盘价把这个仓位算了进去。如果只想看已实现的结果,当最后一笔交易的出场信号是 Open 或为空时,就把它去掉;在 PineForge 的列表里,它就是标着 open 的那一笔。
  • 比较两份回测之前,先让它们停在同一根 K 线上:在 TradingView 结束日期 UTC 零点或之前开盘的最后一根。

本文数据于 2026-10-02 测得,使用 PineForge 引擎和 codegen 1.0.1,即当天发布的版本。

延伸阅读