OPEN-SOURCE SCRIPT
Signal Edge Tester - Backtest Any Indicator vs Random [Zofesu]

Signal Edge Tester - Backtest Any Indicator vs Random [Zofesu] is an overlay tool that takes a signal from any indicator on your chart and tests it against random chance on the same instrument, the same timeframe and the same history.
It answers two separate questions. Does the signal beat random entries — is there skill? And does it make money after costs — is it tradable? A signal can have one without the other, and the tool tells you which.
The statistics are not new, and it is worth being clear about that. Judging a trading rule against random entries rather than against zero is an established idea in evidence-based technical analysis, and shifting a signal through time to build a null distribution is a standard permutation technique. The original part of this script is how these pieces are put together for everyday use: a universal signal input, a baseline that takes a trade from every bar, a correction for clustered signals, a locked out-of-sample test with an embargo, and a verdict that separates skill from trend-driven profit.
It places no trades, gives no signals and predicts nothing. It measures what a signal would have done on the history in front of you.
─────────────────────────────────────
01 — What is Signal Edge Tester?
─────────────────────────────────────
Most indicators show where a signal fired. Very few show whether that signal was better than entering at a random bar.
This tool reads the output of another indicator, turns each signal into a trade with a fixed and pre-defined exit, and compares the average result with what the same trade would have produced from every bar in the sample. It then shifts the whole signal pattern through time many times to measure how large a difference luck alone can produce.
The result is a table with the numbers behind the test and a plain-word verdict in the last row.
─────────────────────────────────────
02 — Connecting a Signal
─────────────────────────────────────
The signal is connected through the source inputs. The tool can read:
Triggers define WHEN the signal is active, not its direction. Direction is set by where you connect the source: the LONG section or the SHORT section.
Value > 0 — arrows, markers and states. Also used for SHORT when the source is an arrow.
Value < 0 — only for a ±1 series where −1 means short.
Crosses above / below level — oscillators with a fixed level.
Close crosses source up / down — moving levels. The source is the level itself.
An entry is created only when the state changes from inactive to active. A condition that stays true for ten bars produces one entry, not ten.
─────────────────────────────────────
03 — How a Trade Is Measured
─────────────────────────────────────
Entry is always the open of the bar AFTER the signal bar. The signal bar is closed before anything is taken, so no information from inside that bar is used.
Mode A — Horizon
The trade closes at the close of the H-th bar from entry. Simple and neutral: it measures where price went, without any target logic.
Mode B — ATR take-profit / stop-loss
Take-profit and stop-loss are set in ATR multiples, using the ATR of the signal bar. If neither is reached, the trade closes after the maximum holding period. Two conservative rules apply:
Costs are subtracted per round trip (entry and exit together), as a percentage of price or in price units. Swaps and overnight financing are not included.
Choose the exit mode BEFORE you look at results. Trying several modes and keeping the best one produces false success.
─────────────────────────────────────
04 — The Random Baseline
─────────────────────────────────────
The baseline takes exactly the same trade — same entry rule, same exit, same costs — from every single bar in the sample. This is the exact limit of infinitely many random entries, so it has no random noise of its own and gives the same answer every time.
Baseline filter
Some signals only fire in a particular regime, for example only above a 200-period moving average. Comparing them with every bar would let the regime itself look like signal skill. With the filter on, the baseline takes entries only from bars where the filter is active, so the signal is compared with random entries in the same regime.
Signals that fire outside the filter are excluded from the test, because the baseline contains no comparable bars for them. The table states this whenever the filter is on, and the entry markers on the chart show only the signals that were actually tested.
─────────────────────────────────────
05 — Cluster Correction
─────────────────────────────────────
Signals rarely arrive one by one. They come in bursts, and trades that overlap in time share the same price move. Counting them as independent pieces of evidence makes any result look more certain than it is.
To handle this, the whole signal pattern is shifted through time — up to 5,000 times — keeping the exact spacing between signals. Each shifted copy gives an average result, and the spread of those results is the band of luck for this particular signal.
Percentile vs random — the share of shifted copies that did worse than the real signal. 50 means the signal is indistinguishable from random placement.
Effective N — the number of trades after removing the effect of clustering. If signals are spread out, Effective N equals N. If they cluster, it drops.
With many signals the number of shifts is reduced automatically so the script stays inside the platform's loop time limit.
─────────────────────────────────────
06 — In-Sample and Out-of-Sample
─────────────────────────────────────
The history is split into two parts.
In-sample (IS) — used for tuning the signal. Its column is diagnostic only.
Out-of-sample (OOS) — the final test. Only this column gives the verdict.
The OOS column is locked by default. Unlock it only when your settings are final. Once you have seen the OOS result and change anything, the OOS has effectively become in-sample and the verdict has lost its value.
Embargo — an in-sample trade is not allowed to end inside the out-of-sample part, so no price move is shared between the two.
Threshold — 95 when one direction is tested, 97.5 when both LONG and SHORT are tested, because two tests double the chance of a lucky result.
─────────────────────────────────────
07 — The Verdict
─────────────────────────────────────
PASSED — the signal beats random entries, its result after costs is positive beyond the band of luck, and the excess over random alone covers the costs.
PASSED · trend-assisted — the signal beats random and is profitable after costs, but the excess over random alone does not cover the costs. Part of the profit comes from the instrument's overall direction during the sample.
INCONCLUSIVE · edge without profit — the signal beats random, but the result after costs is not reliably positive.
INCONCLUSIVE · profit from trend only — the result after costs is positive, but random entries would have done about as well.
INCONCLUSIVE · too little evidence — Effective N is below the minimum. No conclusion is possible.
INCONCLUSIVE — no statistically meaningful difference in either direction.
FALSIFIED · worse than random — the signal did significantly worse than random entries.
FALSIFIED · certain loss — the result after costs is negative beyond the band of luck.
─────────────────────────────────────
08 — Reading the Table
─────────────────────────────────────
All results are shown as a percentage of the entry price, per trade.
Trades (N) — number of tested signal trades.
Effective N (declustered) — see section 05.
Signal avg % (gross) — average signal trade before costs.
Random avg % — average baseline trade before costs.
Percentile vs random — see section 05.
Net avg % (after costs) — average signal trade after costs.
Net interval % — the band of luck around the net result.
Excess after costs % (no trend) — signal minus random minus costs, with its band. This is what the signal would earn in a market with no overall direction.
Verdict — see section 07.
The rows below the verdict show the exit mode, the OOS start date, the costs and the baseline type, so a screenshot of the table is a complete record of the test.
─────────────────────────────────────
09 — Settings
─────────────────────────────────────
LONG / SHORT signal
Test switch, source, trigger and level for each direction. Default: LONG on, SHORT off.
Baseline filter — default off
Filter source and the condition under which a bar belongs to the baseline: Close above / below source, Value > 0 / < 0. Evaluated on the signal bar.
Trade exit
Primary mode — default A · Horizon
A · Horizon — default 10 bars
B · ATR length — default 14
B · TP × ATR — default 2.0
B · SL × ATR — default 1.0
B · Max holding — default 50 bars
Costs
Unit — default % of price
Cost per round trip — default 0.02
Rough guide: forex and indices 0.01–0.02, gold and oil 0.02–0.04, stocks 0.03–0.05, crypto 0.08–0.15.
In-sample / Out-of-sample
OOS boundary — default Last X %
OOS = last X % — default 30
OOS start — used with Fixed date. If the date lies outside the loaded history, Last X % is used and the table shows a warning.
Auto data start from source — default on. Counting starts at the first bar where the signal source has a value, which handles indicators with limited history.
Limit data start — default on, from 1929-01-01. Excludes early history with poor data, such as SPX before 1928 with only a few bars per year. On any history that starts after 1929 it has no effect.
Unlock OOS — default off.
Statistics
Shifts — default 1000
Min. effective trades — default 30
Table
Position, text size, entry markers and OOS boundary line.
─────────────────────────────────────
10 — How To Use
─────────────────────────────────────
Step 1 — Add the indicator you want to test
Both scripts must be on the same chart. The signal must be a plot or an alert condition.
Step 2 — Connect the source and choose the trigger
Check the triangles on the chart: each one must sit on the bar right after your signal. If they do not, the trigger is wrong.
Step 3 — Fix the exit mode and costs before looking at results
Decide how the trade is closed and what it costs on your broker, then leave it.
Step 4 — Tune on in-sample only
Adjust your signal while the OOS column is locked. If the regime matters, turn on the baseline filter.
Step 5 — Unlock the OOS once
When the settings are final, unlock the OOS and read the verdict row. That is the result.
Step 6 — Repeat per instrument and timeframe
A verdict describes one instrument on one timeframe. A PASSED on one symbol says nothing about another.
Step 7 — Switching to another indicator
While a source is connected, the tester is linked to that indicator. If you remove the indicator from the chart, the tester is removed with it. To test something else, first connect the tester to the new indicator, or press Reset settings in the tester. Only then remove the old indicator.
─────────────────────────────────────
11 — Limitations
─────────────────────────────────────
The publication chart shows the tester on SPX D1, connected to the built-in RSI with length 2 and testing RSI crossing below 10 as a LONG signal. The lower band of the RSI is moved to 10 so the tested level is visible. Exit and cost settings are the defaults. Two inputs differ from the defaults, because the tool cannot show anything without them: the signal source, trigger and level are connected to the RSI, and the out-of-sample column is unlocked so both columns are visible. In your own use, keep the OOS locked until your settings are final.
The chart is also a lesson in itself. The same signal is worse than random in the in-sample years (1929–1997) and passes in the out-of-sample years (1997 onward). A verdict belongs to one instrument and one period, not to the signal forever.
Works on all asset classes: Indices, Forex, Gold, Oil, Stocks, Crypto.
Works on all timeframes.
It answers two separate questions. Does the signal beat random entries — is there skill? And does it make money after costs — is it tradable? A signal can have one without the other, and the tool tells you which.
The statistics are not new, and it is worth being clear about that. Judging a trading rule against random entries rather than against zero is an established idea in evidence-based technical analysis, and shifting a signal through time to build a null distribution is a standard permutation technique. The original part of this script is how these pieces are put together for everyday use: a universal signal input, a baseline that takes a trade from every bar, a correction for clustered signals, a locked out-of-sample test with an embargo, and a verdict that separates skill from trend-driven profit.
It places no trades, gives no signals and predicts nothing. It measures what a signal would have done on the history in front of you.
─────────────────────────────────────
01 — What is Signal Edge Tester?
─────────────────────────────────────
Most indicators show where a signal fired. Very few show whether that signal was better than entering at a random bar.
This tool reads the output of another indicator, turns each signal into a trade with a fixed and pre-defined exit, and compares the average result with what the same trade would have produced from every bar in the sample. It then shifts the whole signal pattern through time many times to measure how large a difference luck alone can produce.
The result is a table with the numbers behind the test and a plain-word verdict in the last row.
─────────────────────────────────────
02 — Connecting a Signal
─────────────────────────────────────
The signal is connected through the source inputs. The tool can read:
- an arrow or marker drawn with plotshape (read as 1 when present, 0 when not)
- a state series or a ±1 series
- an alert condition
- an oscillator, together with a level (for example RSI and 30)
- a moving level such as VWAP, a moving average or a value area boundary
Triggers define WHEN the signal is active, not its direction. Direction is set by where you connect the source: the LONG section or the SHORT section.
Value > 0 — arrows, markers and states. Also used for SHORT when the source is an arrow.
Value < 0 — only for a ±1 series where −1 means short.
Crosses above / below level — oscillators with a fixed level.
Close crosses source up / down — moving levels. The source is the level itself.
An entry is created only when the state changes from inactive to active. A condition that stays true for ten bars produces one entry, not ten.
─────────────────────────────────────
03 — How a Trade Is Measured
─────────────────────────────────────
Entry is always the open of the bar AFTER the signal bar. The signal bar is closed before anything is taken, so no information from inside that bar is used.
Mode A — Horizon
The trade closes at the close of the H-th bar from entry. Simple and neutral: it measures where price went, without any target logic.
Mode B — ATR take-profit / stop-loss
Take-profit and stop-loss are set in ATR multiples, using the ATR of the signal bar. If neither is reached, the trade closes after the maximum holding period. Two conservative rules apply:
- if one bar touches both take-profit and stop-loss, the stop-loss is counted
- a gap through the stop-loss exits at the open, not at the stop price
Costs are subtracted per round trip (entry and exit together), as a percentage of price or in price units. Swaps and overnight financing are not included.
Choose the exit mode BEFORE you look at results. Trying several modes and keeping the best one produces false success.
─────────────────────────────────────
04 — The Random Baseline
─────────────────────────────────────
The baseline takes exactly the same trade — same entry rule, same exit, same costs — from every single bar in the sample. This is the exact limit of infinitely many random entries, so it has no random noise of its own and gives the same answer every time.
Baseline filter
Some signals only fire in a particular regime, for example only above a 200-period moving average. Comparing them with every bar would let the regime itself look like signal skill. With the filter on, the baseline takes entries only from bars where the filter is active, so the signal is compared with random entries in the same regime.
Signals that fire outside the filter are excluded from the test, because the baseline contains no comparable bars for them. The table states this whenever the filter is on, and the entry markers on the chart show only the signals that were actually tested.
─────────────────────────────────────
05 — Cluster Correction
─────────────────────────────────────
Signals rarely arrive one by one. They come in bursts, and trades that overlap in time share the same price move. Counting them as independent pieces of evidence makes any result look more certain than it is.
To handle this, the whole signal pattern is shifted through time — up to 5,000 times — keeping the exact spacing between signals. Each shifted copy gives an average result, and the spread of those results is the band of luck for this particular signal.
Percentile vs random — the share of shifted copies that did worse than the real signal. 50 means the signal is indistinguishable from random placement.
Effective N — the number of trades after removing the effect of clustering. If signals are spread out, Effective N equals N. If they cluster, it drops.
With many signals the number of shifts is reduced automatically so the script stays inside the platform's loop time limit.
─────────────────────────────────────
06 — In-Sample and Out-of-Sample
─────────────────────────────────────
The history is split into two parts.
In-sample (IS) — used for tuning the signal. Its column is diagnostic only.
Out-of-sample (OOS) — the final test. Only this column gives the verdict.
The OOS column is locked by default. Unlock it only when your settings are final. Once you have seen the OOS result and change anything, the OOS has effectively become in-sample and the verdict has lost its value.
Embargo — an in-sample trade is not allowed to end inside the out-of-sample part, so no price move is shared between the two.
Threshold — 95 when one direction is tested, 97.5 when both LONG and SHORT are tested, because two tests double the chance of a lucky result.
─────────────────────────────────────
07 — The Verdict
─────────────────────────────────────
PASSED — the signal beats random entries, its result after costs is positive beyond the band of luck, and the excess over random alone covers the costs.
PASSED · trend-assisted — the signal beats random and is profitable after costs, but the excess over random alone does not cover the costs. Part of the profit comes from the instrument's overall direction during the sample.
INCONCLUSIVE · edge without profit — the signal beats random, but the result after costs is not reliably positive.
INCONCLUSIVE · profit from trend only — the result after costs is positive, but random entries would have done about as well.
INCONCLUSIVE · too little evidence — Effective N is below the minimum. No conclusion is possible.
INCONCLUSIVE — no statistically meaningful difference in either direction.
FALSIFIED · worse than random — the signal did significantly worse than random entries.
FALSIFIED · certain loss — the result after costs is negative beyond the band of luck.
─────────────────────────────────────
08 — Reading the Table
─────────────────────────────────────
All results are shown as a percentage of the entry price, per trade.
Trades (N) — number of tested signal trades.
Effective N (declustered) — see section 05.
Signal avg % (gross) — average signal trade before costs.
Random avg % — average baseline trade before costs.
Percentile vs random — see section 05.
Net avg % (after costs) — average signal trade after costs.
Net interval % — the band of luck around the net result.
Excess after costs % (no trend) — signal minus random minus costs, with its band. This is what the signal would earn in a market with no overall direction.
Verdict — see section 07.
The rows below the verdict show the exit mode, the OOS start date, the costs and the baseline type, so a screenshot of the table is a complete record of the test.
─────────────────────────────────────
09 — Settings
─────────────────────────────────────
LONG / SHORT signal
Test switch, source, trigger and level for each direction. Default: LONG on, SHORT off.
Baseline filter — default off
Filter source and the condition under which a bar belongs to the baseline: Close above / below source, Value > 0 / < 0. Evaluated on the signal bar.
Trade exit
Primary mode — default A · Horizon
A · Horizon — default 10 bars
B · ATR length — default 14
B · TP × ATR — default 2.0
B · SL × ATR — default 1.0
B · Max holding — default 50 bars
Costs
Unit — default % of price
Cost per round trip — default 0.02
Rough guide: forex and indices 0.01–0.02, gold and oil 0.02–0.04, stocks 0.03–0.05, crypto 0.08–0.15.
In-sample / Out-of-sample
OOS boundary — default Last X %
OOS = last X % — default 30
OOS start — used with Fixed date. If the date lies outside the loaded history, Last X % is used and the table shows a warning.
Auto data start from source — default on. Counting starts at the first bar where the signal source has a value, which handles indicators with limited history.
Limit data start — default on, from 1929-01-01. Excludes early history with poor data, such as SPX before 1928 with only a few bars per year. On any history that starts after 1929 it has no effect.
Unlock OOS — default off.
Statistics
Shifts — default 1000
Min. effective trades — default 30
Table
Position, text size, entry markers and OOS boundary line.
─────────────────────────────────────
10 — How To Use
─────────────────────────────────────
Step 1 — Add the indicator you want to test
Both scripts must be on the same chart. The signal must be a plot or an alert condition.
Step 2 — Connect the source and choose the trigger
Check the triangles on the chart: each one must sit on the bar right after your signal. If they do not, the trigger is wrong.
Step 3 — Fix the exit mode and costs before looking at results
Decide how the trade is closed and what it costs on your broker, then leave it.
Step 4 — Tune on in-sample only
Adjust your signal while the OOS column is locked. If the regime matters, turn on the baseline filter.
Step 5 — Unlock the OOS once
When the settings are final, unlock the OOS and read the verdict row. That is the result.
Step 6 — Repeat per instrument and timeframe
A verdict describes one instrument on one timeframe. A PASSED on one symbol says nothing about another.
Step 7 — Switching to another indicator
While a source is connected, the tester is linked to that indicator. If you remove the indicator from the chart, the tester is removed with it. To test something else, first connect the tester to the new indicator, or press Reset settings in the tester. Only then remove the old indicator.
─────────────────────────────────────
11 — Limitations
─────────────────────────────────────
- Only plots and alert conditions are readable. Lines, boxes and labels drawn by another script are invisible to it.
- It cannot see inside another script. If the tested indicator repaints or uses future data, the verdict inherits that error.
- Swaps and overnight financing are not included.
- The statistics are built from the history your chart has loaded. Two users on the same symbol can see different numbers if one has scrolled back further.
- The verdict is a measurement of the past, not a forecast, and it is not a promise of future results.
The publication chart shows the tester on SPX D1, connected to the built-in RSI with length 2 and testing RSI crossing below 10 as a LONG signal. The lower band of the RSI is moved to 10 so the tested level is visible. Exit and cost settings are the defaults. Two inputs differ from the defaults, because the tool cannot show anything without them: the signal source, trigger and level are connected to the RSI, and the out-of-sample column is unlocked so both columns are visible. In your own use, keep the OOS locked until your settings are final.
The chart is also a lesson in itself. The same signal is worse than random in the in-sample years (1929–1997) and passes in the out-of-sample years (1997 onward). A verdict belongs to one instrument and one period, not to the signal forever.
Works on all asset classes: Indices, Forex, Gold, Oil, Stocks, Crypto.
Works on all timeframes.
Скрипт с открытым кодом
В истинном духе TradingView, создатель этого скрипта сделал его открытым исходным кодом, чтобы трейдеры могли проверить и убедиться в его функциональности. Браво автору! Вы можете использовать его бесплатно, но помните, что перепубликация кода подчиняется нашим Правилам поведения.
Think Act Believe. Use SL. Use BreakEven. Respect the market. Your ego and arrogance can destroy your trading account. Never leave the table unless you have a closed trade or SL/BE. Indicator does not guarantee profit.
discord.gg/FcSJpSvNnv
discord.gg/FcSJpSvNnv
Отказ от ответственности
Информация и публикации не предназначены для предоставления и не являются финансовыми, инвестиционными, торговыми или другими видами советов или рекомендаций, предоставленных или одобренных TradingView. Подробнее читайте в Условиях использования.
Скрипт с открытым кодом
В истинном духе TradingView, создатель этого скрипта сделал его открытым исходным кодом, чтобы трейдеры могли проверить и убедиться в его функциональности. Браво автору! Вы можете использовать его бесплатно, но помните, что перепубликация кода подчиняется нашим Правилам поведения.
Think Act Believe. Use SL. Use BreakEven. Respect the market. Your ego and arrogance can destroy your trading account. Never leave the table unless you have a closed trade or SL/BE. Indicator does not guarantee profit.
discord.gg/FcSJpSvNnv
discord.gg/FcSJpSvNnv
Отказ от ответственности
Информация и публикации не предназначены для предоставления и не являются финансовыми, инвестиционными, торговыми или другими видами советов или рекомендаций, предоставленных или одобренных TradingView. Подробнее читайте в Условиях использования.