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.
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
The headline result
The four best-performing indicator types are not indicators. PortfolioValue, MaxDrawdown, PositionValue and PositionPercentChange all describe the state of the position you already hold, not the shape of the price line.
Nothing in the top four 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.
The reliability floor
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 34. The other 59 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
34
500+ backtests each
Backtests
222,010
traded, hand-authored
The ranking
All 34 types that clear the floor
One rule is reliably bad
Two types carry a negative median Sharpe, at -0.094 for PositionMaxDrawdown and -0.571 for MinutesAfterOpen, but only one of them survives its own interval. MinutesAfterOpen sits at [-0.694, -0.348] and is genuinely below zero. PositionMaxDrawdown sits at [-0.225, 0.009], so it straddles zero and I cannot tell you it is negative. A negative Sharpe also means returns trailed the risk-free rate once volatility is accounted for, which is not the same as losing money.
MinutesAfterOpen carries one more caveat that cuts against comparing it with the rest: strategies timing entries by minutes after the open are intraday, so they face a different cost and horizon profile than the daily strategies filling most of this table. Some of that -0.571 is the timeframe rather than the rule.
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.
Two of those four are closer to one finding than two. PositionValue and PositionPercentChange post an identical 0.927 because they largely appear in the same strategies: 10,407 of the 14,839 backtests using PositionPercentChange also use PositionValue. PortfolioValue and MaxDrawdown are not like that, overlapping in only 72 of 3,150, and both hold their place when I count one observation per portfolio instead of per backtest, at 1.276 and 1.182 across 3,068 and 1,357 distinct portfolios. The top of this table is a top two and a pair.
Show all 93 observed indicator types
Every built-in type observed in the corpus, including the 59 that fall below the 500-backtest floor. Rows in grey are descriptive only: they are too rare to rank fairly, and no claim here rests on them.
| # | Indicator | Backtests | Median Sharpe |
|---|---|---|---|
| 1 | PortfolioValue | 3,150 | 1.244 |
| 2 | SumOrderAmount | 4 | 1.135 |
| 3 | MaxDrawdown | 1,637 | 1.121 |
| 4 | PositionValue | 31,969 | 0.927 |
| 5 | PositionPercentChange | 14,839 | 0.927 |
| 6 | MaxDrawup | 84 | 0.923 |
| 7 | CurrentTimeSeconds | 2 | 0.876 |
| 8 | Multiply | 4,547 | 0.863 |
| 9 | OptionGrossExposurePercent | 1,529 | 0.859 |
| 10 | IndicatorExponentialMovingAverage | 341 | 0.852 |
| 11 | Value | 117,723 | 0.851 |
| 12 | LastOrderPrice | 432 | 0.769 |
| 13 | IndicatorStandardDeviation | 49 | 0.755 |
| 14 | Month | 108 | 0.747 |
| 15 | DaysSinceLastRebalanceOptionOrder | 2,385 | 0.742 |
| 16 | OptionDaysHeld | 17 | 0.738 |
| 17 | PriceRateOfChange | 5,132 | 0.730 |
| 18 | DaysSinceStrategyFired | 934 | 0.713 |
| 19 | IndicatorSimpleMovingAverage | 1,153 | 0.673 |
| 20 | ExponentialMovingAverage | 2,137 | 0.646 |
| 21 | SimpleMovingAverage | 21,634 | 0.643 |
| 22 | DaysSinceOrder | 44,138 | 0.640 |
| 23 | MinutesUntilClose | 1,499 | 0.630 |
| 24 | Date | 206 | 0.623 |
| 25 | Price | 16,952 | 0.602 |
| 26 | OptionPositionCount | 2,315 | 0.600 |
| 27 | BearishFairValueGap | 3 | 0.581 |
| 28 | TrailingSum | 13 | 0.537 |
| 29 | BuyingPower | 213 | 0.532 |
| 30 | Index | 4,168 | 0.529 |
| 31 | Day | 842 | 0.455 |
| 32 | PreviousClosingPrice | 9 | 0.419 |
| 33 | Divide | 724 | 0.411 |
| 34 | PriceStandardDeviation | 681 | 0.408 |
| 35 | Log | 11 | 0.402 |
| 36 | DaysSinceOptionOrder | 1,294 | 0.402 |
| 37 | StockReport | 111 | 0.397 |
| 38 | PriceMeanAbsoluteDeviation | 25 | 0.396 |
| 39 | AbsoluteValue | 67 | 0.379 |
| 40 | RelativeStrengthIndex | 8,626 | 0.373 |
| 41 | Year | 37 | 0.355 |
| 42 | Plus | 850 | 0.353 |
| 43 | IndicatorRateOfChange | 748 | 0.347 |
| 44 | OptionPositionValue | 7 | 0.344 |
| 45 | Minus | 1,598 | 0.330 |
| 46 | DaysSinceEarnings | 5 | 0.288 |
| 47 | DaysSinceAgent | 507 | 0.265 |
| 48 | PriceChangeSinceOpen | 236 | 0.233 |
| 49 | MinutesSinceOptionOrder | 441 | 0.224 |
| 50 | CurrentTimeHours | 120 | 0.205 |
| 51 | BollingerBand | 1,107 | 0.198 |
| 52 | OptionPositionPercentChange | 218 | 0.179 |
| 53 | InitialValue | 46 | 0.175 |
| 54 | CompoundAnnualGrowthRate | 6 | 0.158 |
| 55 | ConsecutiveTrue | 60 | 0.145 |
| 56 | IndicatorMeanAbsoluteDeviation | 4 | 0.137 |
| 57 | Max | 23 | 0.132 |
| 58 | IsIndustry | 8 | 0.130 |
| 59 | Fundamental | 618 | 0.101 |
| 60 | OptionSpreadCount | 4,360 | 0.094 |
| 61 | MaximumPrice | 243 | 0.074 |
| 62 | IsIndexMember | 15 | 0.063 |
| 63 | Economic | 32 | 0.054 |
| 64 | MinutesSinceAgent | 248 | 0.054 |
| 65 | CrossAbove | 1,364 | 0.045 |
| 66 | CrossBelow | 913 | 0.027 |
| 67 | OptionDaysToExpiration | 27 | -0.016 |
| 68 | CurrentTimeMinutes | 152 | -0.091 |
| 69 | PositionMaxDrawdown | 962 | -0.094 |
| 70 | OptionUnrealizedPnL | 120 | -0.113 |
| 71 | MinimumPrice | 184 | -0.200 |
| 72 | GapPercentage | 49 | -0.210 |
| 73 | CountTrue | 60 | -0.336 |
| 74 | AverageTrueRange | 67 | -0.449 |
| 75 | UnderlyingMaxDrawdown | 129 | -0.536 |
| 76 | BullishFairValueGap | 22 | -0.541 |
| 77 | MinutesAfterOpen | 600 | -0.571 |
| 78 | PositionMaxDrawup | 50 | -0.724 |
| 79 | VWAP | 167 | -0.732 |
| 80 | DaysSinceAlert | 53 | -0.952 |
| 81 | DaysUntilEarnings | 71 | -0.994 |
| 82 | IndicatorWindowAgo | 146 | -1.148 |
| 83 | OptionPositionMaxDrawdown | 28 | -1.158 |
| 84 | IsAsset | 1 | -1.200 |
| 85 | IndicatorAtMinutesAfterOpen | 115 | -1.332 |
| 86 | Negative | 24 | -1.628 |
| 87 | DaysSinceTransaction | 4 | -1.858 |
| 88 | MinutesSinceOrder | 119 | -2.724 |
| 89 | SumOrderQuantity | 30 | -3.031 |
| 90 | HighOfDay | 9 | -4.404 |
| 91 | OpeningPrice | 10 | -4.650 |
| 92 | LowOfDay | 4 | -5.656 |
| 93 | Volume | 15 | -13.744 |
Where this goes next
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 34 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, it answers the question, and the answer is no.
My first pass at this bucketed every backtest by every window it contained, which counted crossover strategies twice and produced a 0.261 spread I was ready to publish as a finding. Restricted to strategies using a single window, the spread is 0.038 and every pairwise difference straddles zero: a 20-day average beats a 200-day one by 0.045 with an interval of [-0.035, +0.098]. I cannot show you that window length matters.
That is a real answer rather than a failed one, and it cuts both ways. The three neighbouring types are just as unresolved, so the fine structure in the middle of the ranking is mostly noise. What survives is the top of the table, where the intervals are tight and separated, and the bottom, where MinutesAfterOpen is clearly below everything else. Between those, the honest reading is that this survey cannot tell a 50-day average from a 200-day one, and a controlled sweep can.
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.
Corrections
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 still not testable, for a better reason. Among strategies using a single window the 200-day average medians 0.539 against 0.577 for a 20-day one, and that difference sits inside its own confidence interval. The 200-day average is not the most powerful indicator, and it is not the worst either. I cannot separate it from any other window.
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.
A second result
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.
Methodology
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. 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.
The archive behind every number here is queryable by the platform's AI agent, and so is the sweep that turns a correlation into an experiment. Ask it which indicator ranked first, or hold your own strategy fixed and test one rule in isolation, without writing any code.
Ask which indicator wonEvery 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.
Keep reading
Create a free account to finish this article
Free forever. Read every article, ask Aurora about any strategy in it, and backtest the idea against market history.
No credit card required. Already have an account?
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.
Free to browse. No credit card required.
No comments yet.