← All Articles
An abstract field of soft green and gold light drifting over a scatter of points, standing in for the 222,010 backtests behind the ranking.

NexusTrade · Research · 222,010 backtests

The best and worst trading indicators, ranked across 222,010 backtests. The top 4 are not what you expect.

I ranked all 93 indicator types on the platform by median Sharpe ratio, then went after the harder questions a corpus this size makes answerable.

Austin Starks Austin Starks ✦ Founder, NexusTrade ✦ September 7, 2026 ✦ 9 min read

In November 2025 I ranked every indicator across 114,549 backtests and published what I found. The corpus has since grown to 222,010, so I ran it again. The top of the list is not where most people would look for it.

Previously · I Analyzed 100,000 Backtests to Find the Best Trading Indicator. The Answer Was Not What I Expected

Watch · the 47-second version of this, on RSI

The headline result

The four best-performing types are not chart indicators. PortfolioValue, MaxDrawdown and PositionValue describe the state of the position you already hold, and DaysSinceStrategyFired is a clock. The best-performing thing that reads the price line is PriceRateOfChange, in ninth.

Nothing in the top eight is a chart pattern. The best risk-adjusted returns came from strategies that checked their own position before acting. Read that as a fact about the people who write these strategies as much as about the indicators: nobody was randomly assigned a rule, and someone reaching for MaxDrawdown is already thinking about risk. This is a survey of what worked for whoever wrote it, not an experiment.

What "enough data to rank" means

The corpus contains 93 observed built-in indicator types. Most of them are useless for ranking, because a type used in nine backtests tells you about nine people, not about the indicator.

So there is a reliability floor: a type only enters the ranking once it appears in at least 500 backtests. That cuts 93 down to 31. The rest are reported in the full table but never used to make a claim.

Everything is a median Sharpe ratio across traded, hand-authored backtests lasting 365 to 2,000 days. Optimizer-generated runs are excluded, because a genetic search is a population of attempts rather than strategies anyone chose to keep. That exclusion alone removes 74,518 runs.

Observed types

93

built-in indicators

Clear the floor

31

500+ experiments each

Backtests

222,010

traded, hand-authored

All 31 types that clear the floor

MEDIAN SHARPE · 31 TYPES WITH 500+ EXPERIMENTS · 95% BOOTSTRAP INTERVAL01PortfolioValue0.9261,53902MaxDrawdown0.82476803DaysSinceStrategyFired0.78950904PositionValue0.75716,50805Value0.74256,95406Multiply0.7373,27007DaysSinceOrder0.70321,85808PositionPercentChange0.6947,95109PriceRateOfChange0.6254,02510SimpleMovingAverage0.61514,20111OptionPositionCount0.5711,24112ExponentialMovingAverage0.5641,94613Day0.55062214MinutesUntilClose0.53670515Price0.51513,30516PriceStandardDeviation0.40267417DaysSinceLastRebalanceOptionOrder0.39352518Divide0.37765919Index0.3722,36420DaysSinceOptionOrder0.35281321Plus0.34882922RelativeStrengthIndex0.3177,05623Minus0.2891,51024IndicatorRateOfChange0.27858625BollingerBand0.1821,07626IndicatorSimpleMovingAverage0.15583527Fundamental0.07158228CrossBelow0.00881429CrossAbove0.0071,17930OptionSpreadCount-0.0332,98231PositionMaxDrawdown-0.128892◆ portfolio state △ operator ○ timing (unmarked) chart signal0experiments
Every type that cleared the 500-backtest floor. Whiskers are 95% bootstrap intervals on the median. Where they overlap, the ordering between those rows is not resolved by this sample. Marks separate the kinds of thing being ranked: many rows are portfolio-state readings or DSL operators rather than chart signals.

The worst rule is a risk rule

PositionMaxDrawdown is last at -0.128, and its interval of [-0.248, -0.027] stays below zero, so this one is genuinely negative rather than merely low. A negative Sharpe means returns trailed the risk-free rate once volatility is accounted for, which is not the same as losing money.

MaxDrawdown sits second and PositionMaxDrawdown sits last, which looks like one measurement contradicting itself. It is not one measurement. MaxDrawdown takes a window and an asset, so it asks how far a stock has fallen from its peak, and most of its uses drive a Buy. PositionMaxDrawdown has no window and only exists once you hold something, and almost all of its uses drive a Sell. One buys weakness and the other sells it.

Sell rules do score lower in general, but that is not the explanation: among them PositionMaxDrawdown is second worst of eight, against 0.787 for DaysSinceOrder. The sharpest comparison is PositionPercentChange, which is also profit and loss on an open position and also drives sells. Selling when the position is up 10% or more medians 0.711 across 7,313 experiments. Selling when it is down about 6% medians 0.186. Selling on a 10% drawdown from the position's own peak medians -0.099.

So this is a result about stop-losses rather than about drawdown. Exiting on strength beat exiting on weakness, the trailing version was worst, and the same weakness used as an entry signal sits near the top of the table. It is still a survey, so the sweep that would settle it is the one described above: hold a strategy fixed and change only the exit rule.

MinutesAfterOpen, which the earlier version of this piece called the worst reliable rule, no longer clears the floor once cloned experiments are collapsed. It has too few distinct experiments behind it to rank.

The top four are portfolio state rather than chart signals. They measure how much the portfolio is worth, how far the asset has fallen from its peak, how big the position is, and how far the position is from its cost basis.

PositionValue and PositionPercentChange used to post an identical 0.927 and sit third and fourth. They largely appear in the same strategies, with 10,407 of the 14,839 backtests using PositionPercentChange also using PositionValue, and once cloned experiments are collapsed they separate: PositionValue holds fourth at 0.757 and PositionPercentChange falls to eighth at 0.694. PortfolioValue and MaxDrawdown were never entangled that way: they co-occur in well under 3% of the strategies that use either.

Show all 93 observed indicator types

Every built-in type observed in the corpus, including the 62 that fall below the 500-experiment floor. Rows in grey are descriptive only: they are too rare to rank fairly, and no claim here rests on them.

All 93 observed built-in indicator types
#IndicatorBacktestsMedian Sharpe
1SumOrderAmount41.135
2PortfolioValue1,5390.926
3MaxDrawup840.923
4OptionGrossExposurePercent2470.879
5CurrentTimeSeconds20.876
6MaxDrawdown7680.824
7DaysSinceStrategyFired5090.789
8Month1040.766
9PositionValue16,5080.757
10IndicatorStandardDeviation490.755
11Value56,9540.742
12Multiply3,2700.737
13IndicatorExponentialMovingAverage2940.705
14DaysSinceOrder21,8580.703
15LastOrderPrice2330.702
16PositionPercentChange7,9510.694
17Date1890.632
18PriceRateOfChange4,0250.625
19InitialValue260.617
20SimpleMovingAverage14,2010.615
21BearishFairValueGap30.581
22OptionPositionCount1,2410.571
23PriceChangeSinceOpen1030.571
24ExponentialMovingAverage1,9460.564
25Day6220.550
26TrailingSum130.537
27MinutesUntilClose7050.536
28Year330.533
29BuyingPower1970.532
30Price13,3050.515
31MinutesSinceOptionOrder2110.502
32StockReport960.462
33PreviousClosingPrice50.419
34PriceStandardDeviation6740.402
35PriceMeanAbsoluteDeviation250.396
36DaysSinceLastRebalanceOptionOrder5250.393
37Log100.380
38Divide6590.377
39Index2,3640.372
40AbsoluteValue660.364
41DaysSinceOptionOrder8130.352
42Plus8290.348
43OptionPositionValue70.344
44RelativeStrengthIndex7,0560.317
45Minus1,5100.289
46DaysSinceEarnings50.288
47OptionDaysHeld140.287
48IndicatorRateOfChange5860.278
49DaysSinceAgent4060.267
50BollingerBand1,0760.182
51CompoundAnnualGrowthRate60.158
52IndicatorSimpleMovingAverage8350.155
53IsIndexMember120.140
54IndicatorMeanAbsoluteDeviation40.137
55Max230.132
56IsIndustry80.130
57OptionPositionPercentChange1950.082
58MinutesSinceAgent2070.081
59Fundamental5820.071
60Economic320.054
61CurrentTimeMinutes1360.050
62MaximumPrice2310.040
63CrossBelow8140.008
64CrossAbove1,1790.007
65OptionDaysToExpiration27-0.016
66ConsecutiveTrue56-0.016
67OptionSpreadCount2,982-0.033
68GapPercentage46-0.040
69OptionUnrealizedPnL50-0.113
70PositionMaxDrawdown892-0.128
71MinimumPrice184-0.200
72CountTrue58-0.302
73CurrentTimeHours106-0.381
74BullishFairValueGap22-0.541
75UnderlyingMaxDrawdown90-0.616
76AverageTrueRange61-0.664
77PositionMaxDrawup48-0.724
78VWAP157-0.732
79MinutesAfterOpen429-0.757
80DaysSinceAlert53-0.952
81IndicatorWindowAgo144-1.075
82OptionPositionMaxDrawdown28-1.158
83IsAsset1-1.200
84IndicatorAtMinutesAfterOpen112-1.455
85Negative24-1.628
86DaysUntilEarnings57-1.808
87DaysSinceTransaction4-1.858
88MinutesSinceOrder103-2.957
89SumOrderQuantity30-3.031
90HighOfDay9-4.404
91OpeningPrice10-4.650
92LowOfDay4-5.656
93Volume15-13.744

What a corpus this size is actually for

A ranking is a survey. Nobody was randomly assigned an indicator, every strategy here was written by a person who chose their own tickers and rules, and people reach for different tools on different problems. The ordering above tells you where to look, not what causes what.

222,010 outcomes is enough to stop guessing and start testing. Three things become possible here that a smaller dataset cannot support.

1. Run the experiment instead of the survey

Take one strategy, one universe, one window, one rebalance cadence, and change exactly one thing. The indicator goes in, or it comes out. Everything else is held. Run both.

That is a controlled experiment, and it is a backtest sweep the platform already knows how to run. The ranking's job is telling you where to point it. You cannot sweep everything, and 31 candidates ordered by median Sharpe is a very good prior.

2. Go down to the configuration, not the type

An indicator type is not one rule, and treating it as one rule is the weakness of every ranking including this one. A 200-day moving average used as a trend filter and a 3-day moving average used as a trigger share a name and nothing else. They belong at opposite ends of this table, and right now they sit in the same row.

I said in the last piece that I could not answer this, because the lookback column was empty across all 304,426 SimpleMovingAverage rows. The column was empty, and the reason was a bug of mine. The corpus exporter looked for a top-level lookback field. The window is stored at window.length, so the extractor found nothing and wrote a null for all 4,424,078 indicator rows. Nothing failed loudly, because the export succeeded and the column existed. It was simply always empty, and the value was sitting in the same table the whole time, one column over in the raw parameters.

Read correctly, and with cloned experiments collapsed, it answers the question, and the answer is yes.

THREE NEIGHBOURING TYPESPriceRateOfChange0.625[0.592, 0.656]4,025SimpleMovingAverage0.615[0.599, 0.631]14,201OptionPositionCount0.571[0.497, 0.622]1,241SIMPLEMOVINGAVERAGE BY WINDOW · ONE-WINDOW STRATEGIES ONLY20-day0.595[0.531, 0.640]83650-day0.472[0.408, 0.514]1,872200-day0.346[0.301, 0.394]3,1570median · 95% interval · backtests
Counting only strategies that use exactly one moving-average window, because a 50/200 crossover otherwise lands in both rows and makes one population look like two. 64% of the backtests holding a 200-day average also hold a 50-day one.

This took two corrections to get right, and the first published version of each was wrong. Bucketing every backtest by every window it contained counted crossover strategies twice and produced a 0.261 spread. Fixing that but leaving cloned experiments in place flattened the whole thing to 0.038, with every difference straddling zero, and I published that as a null result. Collapsing clones as well, a 20-day average beats a 200-day one by 0.244 with an interval of [+0.175, +0.310], and all three pairs separate.

So the setting matters more than the type. SimpleMovingAverage sits tenth at 0.613, just below PriceRateOfChange at 0.625, and its neighbours sit within about 0.06 of it. The windows inside SimpleMovingAverage span 0.249, four times that gap, and unlike the neighbours they separate cleanly. Ranking types puts the answer at the wrong level, and the shorter the average the better it did.

3. Train on the outcomes

A strategy paired with what it did is a labelled example. 222,010 of them is a training set, and a rare one: the input is structured rather than prose, the label is a number rather than a judgement, and nobody wrote any of it to be training data.

That is the shape you need to fine-tune a model to write strategies that land in the top decile instead of the middle of the distribution. Every language model can already describe what a good strategy looks like. This would be one that has seen 222,010 attempts and what each of them returned. The obvious hazard is that a model trained to maximise backtest Sharpe learns to overfit backtests, so the label has to be out-of-sample performance rather than the number the corpus already holds.

What twice the data revised

The corpus doubled, so I checked what it broke. Three conclusions from my 2025 analysis of 114,549 backtests did not survive the larger sample.

Contradicted

"More rules, worse results"

The 2025 piece showed complexity dragging Sharpe down and I published a scatter plot to prove it. The larger corpus shows a U shape instead: 1 rule 0.932, 2 rules 0.606, 3 to 4 rules 0.791, 5 to 8 rules 0.927, 9 or more 0.845. The worst place to be is two rules, not nine. The simple version of that finding was wrong.

Contradicted

"The 200-day SMA is the single most powerful indicator"

I was ready to file this one as untestable, because the lookback column was empty across all 304,426 SimpleMovingAverage rows. It was empty because of a bug of mine, described above, and the window was recoverable from the same table all along. Read correctly, it is contradicted. Among deduplicated experiments using a single window, the 200-day average medians 0.346 against 0.595 for a 20-day one, and the intervals do not overlap. The 200-day average is not the most powerful setting of a moving average. It is the weakest of the three that clear the floor, and it is the most popular.

Corrected after publishing

The ranking counted the same experiment thousands of times

A reader asked why PositionValue and PositionPercentChange both showed a confidence interval of exactly [0.927, 0.927]. An interval with no width means a large pile of backtests sharing one value, and there was: 1,075 separate portfolios with byte-identical returns on AAPL over the same window, running fifteen variations of one strategy. Sixty-six Sharpe values were each shared by a hundred or more backtests, covering 32% of the runs behind the first version of this ranking.

That is cloning and forking working as designed, not a calculation error. The error was mine, in treating each copy as an independent observation. It is the same argument as the reliability floor, one level up: a type used in nine backtests tells you about nine people, and a thousand copies of one strategy tell you about one strategy. Counting distinct experiments instead keeps 47.8% of the runs.

Every number above is the corrected version. What moved: PortfolioValue fell from 1.244 to 0.926, PositionPercentChange left the top four for seventh, the floor now clears 31 types rather than 34, and MinutesAfterOpen dropped out entirely. RSI moved from 23rd of 34 to 22nd of 31. The shape of the finding did not change, and the moving-average result reversed twice before it settled, which is in the section above.

Withdrawn

"Momentum for growth stocks, mean reversion for indexes"

This one depends almost entirely on how you classify a strategy. The price-versus-moving-average rule dominates the classifier and the trend-filter cutoff is arbitrary, so the finding moves when the definition moves. It needs a sensitivity sweep before anyone repeats it, including me.

The sell rule

While the ranking was running I split every backtest containing a buy action on whether it also contained a sell action. Strategies that never sold posted a median return of 36.41% against 19.50% for strategies that did. Nearly double, and it holds in both eras rather than being a 2020s bull-market artifact.

Then I restricted the same split to runs carrying a benchmark. Of the strategies that never sold, only 43.3% beat simply holding the asset, with a median alpha of -0.85. Of the strategies that did sell, 57.0% beat it, at +8.77.

What that means

Never selling produces big raw returns and still loses to buying and holding the same asset. The benchmark subset is 4,525 runs and skews recent, so treat it as directional rather than settled.

How this was measured

The source is a snapshot of the NexusTrade backtest archive taken on 6 September 2026, covering 400,203 scanned documents. Every figure above comes from that snapshot under one filter: the backtest was traded, it ran between 365 and 2,000 days, and it was authored by a person rather than the optimizer.

Each backtest counts once per indicator type, so a strategy using the same indicator in four conditions contributes one observation rather than four. Rankings are medians, not means, because a handful of leveraged outliers move a mean and tell you nothing about the typical strategy. Every median carries a 95% bootstrap interval from 4,000 resamples, and where two intervals overlap I do not claim an ordering between them. Counting is by distinct EXPERIMENT rather than by backtest: cloned and forked portfolios mean the same strategy on the same tickers over the same window appears many times, and counting each copy treats one experiment as a thousand independent observations. Keying on the strategy logic, the tickers and the window keeps 47.8% of eligible runs. Window comparisons additionally count only strategies using a single window of that type, because a crossover strategy holds two and would otherwise be counted in both.

Only aggregate results for built-in indicator types appear anywhere in this analysis. Creator-owned custom indicators, their names, their parameters, their identifiers and their underlying strategy logic stay private, and the results-only table they live in cannot be joined back to any strategy, portfolio or user.

Ask the corpus yourself

Every table in this piece is a question I typed in English. The archive is queryable by the platform's AI agent, which writes read-only SQL against the backtest tables and hands back the rows, so "median Sharpe by moving-average window, minimum 500 backtests" is a sentence rather than a query. Ask which setting of an indicator you already use has done best, whether your rule reads better driving a buy or a sell, or how a type performs once you hold the timeframe fixed.

Ask carefully, though, because the agent answers the question you asked. Put that moving-average question to it plainly and it will bucket every backtest by every window it contains, count each 50/200 crossover twice, and handed me a spread I nearly published. Adding "only strategies that use a single window" changes the answer and is the difference between a finding and an artefact. The corpus removes the work of gathering the data. It does not remove the work of asking a clean question.

Point it at your own strategy

The sweep that turns a correlation into an experiment is queryable the same way. Hold your own strategy fixed, change one rule, and run both, without writing any code.

Ask it about a setting you use

Every number here comes from the NexusTrade backtest archive, which is why each row carries its sample size instead of a screenshot of a chart. If you want the anonymized aggregate results to run the ranking yourself, send me the word CORPUS.

Build the strategy this article describes

Create a free account to backtest ideas against market history, inspect the risk, and deploy to paper or live markets when you're ready.

or

Free to browse. No credit card required.

Discussion

Sign in or create a free account to join the discussion.

No comments yet.