Skip to content

How 7,989 backtests are graded

6 min read

Two backtests of the same Pine script rarely agree to the cent, so 'matches TradingView' means nothing until you say what is compared and how close is close enough. What one graded backtest is, how its trades are paired with TradingView’s, what each of the five grades requires, and how a code change is judged.

Many backtesters say they match TradingView. Two backtests of the same Pine script almost never agree to the cent, so the claim means little until you know what was compared and how close counts as close. Our scoreboard puts one of five words on each of 7,989 backtests, and 7,905 of them say "excellent". This post explains what that word and the other four mean.

One backtest, one trade list

The unit is a probe: "a strategy exported from TradingView with its trade list and replayed trade for trade on the same bars" (codegen README). The script keeps its own inputs and defaults, and the grade compares two trade lists: TradingView's, and the one PineForge produces on the same bars.

One script can be a probe on several markets. The 7,989 are 309 of the 312 strategies in the public corpus and 7,680 probes of 741 TradingView scripts, spread over nine markets (crypto, US futures and stocks, an Indian index, a currency pair and gold), each on 15-minute and daily bars (scoreboard).

Pairing the trades

The grader is one public script, verify_corpus.py. It pairs each TradingView trade with the closest unused PineForge trade in the same direction whose entry is within an hour of TradingView's and whose entry price is within 3.00 of it, in the market's own price units (pairing, limits). Two things are then measured: how much of TradingView's list was reproduced, and how close the reproduced trades are.

The first is called coverage, and it is counted against every closed TradingView trade, not just the stretch where both lists overlap. The code says why: the other metrics are computed inside a window the matches themselves define, "so without this gate a run that reproduces almost nothing can grade top-tier on the sliver it happened to reproduce (observed in production: 'excellent' at 0.9% real coverage)" (grader). When coverage became a gate, two corpus strategies dropped to moderate: they reproduced 209 of 224 and 228 of 241 TradingView trades, gaps the old window had hidden.

Five words

GradeTradingView trades reproducedTrade countEntry, exit price (p90)P&L (p90)
excellent≥ 99 %identical and, where TradingView shows two or more distinct entry Signals at one exact time, price and direction, the engine shows as many distinct entries there (distinct-entry identity)within 0.01 %within 1 %
strong≥ 95 %within 6 %0.1 %, 0.5 %within 100 %
moderate≥ 75 %, and ≥ 90 % inside the overlap
weakat least one
minimalnone

"p90" means nine trades in ten are at least that close (thresholds, ladder). A list with at most one unmatched TradingView trade passes the excellent and strong coverage limits, so one trade at the edge of a short export cannot cost the grade. Excellent allows no difference at all in the number of trades. The code's reason: "Percent thresholds can hide dozens of missing/extra trades in large strategies" (grader). Strategies whose exits use a trailing stop are graded on looser limits, exits within 0.05 % and P&L within 100 %, because where a trail fills inside a bar depends on TradingView's sub-bar path (grader).

The worst trade is printed, not graded

The grades read the 90th percentile, not the worst trade. One public strategy shows why; here is the grader on order-harbor-stop-limit-01, trimmed:

  Matched:       471 (100.0% of TV)
  Count delta:             0.0000%  (OK; abs=0)
  PnL         p90 delta:   0.0381%  (OK)
  -- report-only (not gated; MAE/MFE are intrabar-path-limited) --
  Entry/Exit/PnL p100:   0.0011% / 0.0084% / 4.9658%
  -> excellent

Look at the last number before the grade: one trade's P&L is almost 5 % off. Asked for its worst trades (--show-diffs 3), the grader shows TradingView entering this one at 1,970.36 and PineForge at 1,970.35, on a trade that lost about 51 cents. One cent on about 2.5 units is 2.5 cents, and 2.5 cents is 5 % of a 51-cent loss. A grade based on the worst trade would deny this strategy excellent over one cent on one entry; the percentile passes it, and the report still shows the outlier.

Judging a change

A grade describes the code as it is. A change is judged by what it moves: the same probes are graded before and after. The rule asks for no regression on the hard surface. On every other market and timeframe it reads a score across two bands, excellent and excellent+strong: a score above zero passes as long as no probe falls more than one grade out of a band, and exactly zero passes when nothing got worse (README).

Two bands, because each sees moves the other misses. A strategy going from strong to excellent changes only the first count; one going from moderate to strong changes only the second. One change in September moved 32 probes into excellent; two of them had been below strong, so they counted in both bands, and the change scored +34 with no probe moving down. The hard surface is the 15-minute ETH lane: 1,009 probes, the 309 public-corpus strategies among them. No probe there may drop, whatever the change gains elsewhere.

When nothing moves

Many good changes move nothing: a refactor, a new API, a fix for a case no strategy in the set reaches. A documentation-only change needs no sweep (example). A code change that moves no probe into or out of a band scores zero, and under the README's rule it passes as long as nothing got worse: flagged "no-improve-no-regression" when no grade changed, or "improved" when something got better where the bands cannot see it, a strategy rising from weak to moderate for instance. A zero can also hide a loss, and then the change fails: one probe falling from moderate to minimal, below both bands, or one probe's drop cancelling another's gain.

For changes like these, zero movement is the expected result, but it does not prove that nothing else changed, because the five words are coarse. So the engine's CI also pins a hash of every trade list the engine produces on the public corpus: a one-tick slippage change on one strategy "moves its trades and its baseline hash while verify_corpus.py still reports excellent=311, strong=0, anomaly=1" (CI docs).

What this means for you

  • On a gallery page, "excellent" means the same number of trades as TradingView, at least 99 % of TradingView's trades reproduced, and prices and P&L within the table's limits for nine trades in ten. For excellent, where TradingView shows two or more distinct entry Signals at one exact time, price and direction, the engine shows as many distinct entries there (distinct-entry identity). Every gallery strategy is excellent except one declared TradingView-side anomaly; "strong" occurs only in the closed test.
  • If you compare your own backtester with TradingView, compare the same way: pair trades by entry, count coverage over all of TradingView's trades, and read percentiles, not the worst trade.
  • The public part can be re-run: the README's commands build, run and grade the 312 corpus strategies. The closed test is our own measurement of scripts that are not public, most of them community scripts kept private under TradingView's terms.

Grades measured on 2026-09-29 on engine and codegen code that the 1.0.0 releases include. The code of engine and codegen 1.0.1, released 2026-10-02, was graded on the same 7,989 probes with no probe changing grade.

Where to go from here