Reality Check: Does RSI Above 70 Mean Sell?
This is the page the rest of the course is built on. Not because RSI matters especially, but because the process does: take a claim everybody repeats, make it specific enough that it could turn out to be false, measure it on data you control, and then read the answer without leaning on the scale. Everything in Parts 27 to 35 is this page with more machinery.
Budget forty minutes and expect to spend more of it thinking than typing.
The claim
Section titled “The claim”Before doing anything else, notice how much of it is unspecified. Which market? Over what period? Sell what — an existing long position, or sell short? At what price? Held for how long? Compared with what alternative? Judged by what measure?
A statement with all of those blanks cannot be wrong. If price falls afterwards, the claim worked. If price rises for six months and then falls, the claim worked, eventually. A statement that survives every outcome is not knowledge; it is a slogan. The whole of this lesson is the work of turning it into something that can lose.
What RSI actually measures
Section titled “What RSI actually measures”AmiBroker publishes RSI’s internal algorithm, and it is short enough to hold in your head. P
is a Wilder-smoothed average of the up-moves in the close, N the same for the down-moves, and
Pseudocode — not valid AFL
RSI = 100 * P / ( P + N )That is a ratio, not a level. It says how much of the recent total movement of the close went upward, with recent bars weighted more heavily than old ones. It knows nothing about how far price has travelled, what it is worth, where it sits relative to any average, how much volume was involved, or what the bar’s range was — only the sequence of close-to-close differences.
Now consider what a strong advance does to that ratio. On any bar with no down-move, S is
zero, so N is multiplied by (period - 1) / period — for a 14-period RSI, by 13/14. Do that
repeatedly and the denominator collapses.
What consecutive up-closes do to the denominator
| Bar | start | after 5 | after 10 | after 15 | after 20 |
|---|---|---|---|---|---|
N as a fraction of its starting value | 1.00 | 0.69 | 0.48 | 0.33 | 0.23 |
RSI, if P is merely maintained | 50.0 | 59.2 | 67.7 | 75.3 | 81.5 |
So a reading above 70 is the fingerprint of a market that has been going up consistently. The claim, stripped of its vocabulary, is therefore:
Sell markets that have been rising consistently.
That might be excellent advice. It might be terrible. But it is not something the indicator discovered — the indicator is a compact way of describing the condition, and the advice is an assertion bolted onto that description. The word “overbought” performs a sleight of hand: it sounds like a statement about supply and demand, and it is a statement about a ratio.
From a claim to a hypothesis
Section titled “From a claim to a hypothesis”To be testable, the claim needs six things attached to it. Missing any one of them and the result cannot be interpreted.
What has to be specified before a claim can be wrong
- PopulationWhich instruments. "Markets" is not a population; a named list of symbols is.
- PeriodWhich stretch of history, with start and end dates. Different eras give different answers.
- ConditionExactly what qualifies a bar. RSI period, level, and — critically — whether it is a state or an event.
- MeasurementWhat you record afterwards: what quantity, over what horizon, from what price.
- ComparisonWhat the result is measured against. Without a baseline a number means nothing.
- Decision ruleWhat outcome would count as support, and what would count as refutation — written down before you look.
Two of those deserve their own paragraphs, because they are where most amateur tests go wrong.
State or event? RSI( 14 ) > 70 is a state: in a strong advance it is true on eighty
consecutive bars. Cross( RSI( 14 ), 70 ) is an event: true on the single bar the level was
first exceeded. These ask genuinely different questions. The state version asks “what happens
during and after periods of consistent strength”. The event version asks “what happens after the
moment strength is first recognised”. The popular claim is ambiguous between them, which is one
more reason it cannot be evaluated as stated. This lesson tests the state, because that is
the reading most people apply — they look at today’s number, not at when it crossed. The event
version is the first extension at the end.
Compared with what? This is the one that separates a test from an anecdote. Suppose you measure the average 20-day forward return after RSI above 70 and it comes out positive. Is that support for holding? Only if it is better than what an arbitrary bar of the same instrument over the same period would have given you. Equity markets have spent long stretches rising, so a positive average forward return is the normal condition, not an achievement. Every conditional number in this course is reported next to its unconditional baseline, always.
The hypothesis, written out
Section titled “The hypothesis, written out”Population. A named watch list of instruments, fixed before the test is run.
Period. All history available in my database for those symbols, with the first year of each symbol’s data excluded as indicator warm-up, and the last 20 bars excluded because they have no future to measure.
Condition. A bar on which
RSI( 14 ), computed on the close, closed above 70.Measurement. The percentage change from that bar’s close to the close 20 bars later.
Comparison. The same measurement computed over every measurable bar in the same history.
Claim under test. The average 20-bar forward change following qualifying bars is lower than the average following all measurable bars — and the difference is large enough, and consistent enough across symbols, to survive the checks listed later in this lesson.
That is a sentence that can lose. If the conditional average comes out higher, the claim as stated has failed on this data. If it comes out the same, the claim has failed to add anything. Both are useful results.
Choosing the universe and the period
Section titled “Choosing the universe and the period”The universe is not an administrative detail; it is the question. Whatever you point this test at is the population your answer describes, and no wider.
Use a fixed, named watch list. Ten to fifty liquid instruments with long histories is plenty for a first pass. Write the list down before you run anything so that you cannot quietly drop the symbols that give inconvenient answers.
Use adjusted data. A 20-bar forward return computed across an unadjusted split or a large dividend is fiction. If you are not sure whether your history is adjusted, find a symbol that split during your period and look at the chart around that date.
Prefer more history over more symbols. Two decades across twenty instruments contains more independent market conditions than two years across two hundred, and market conditions are what this claim is really about.
Designing the measurement
Section titled “Designing the measurement”The measurement deliberately looks forward. That is legitimate here and fatal elsewhere, so it is worth being explicit about why.
Fragment — not a complete formula
// Ref() with a POSITIVE shift reads a FUTURE bar.ForwardReturn = 100 * ( Ref( Close, 20 ) - Close ) / Close;We are asking “what had already happened, twenty bars after such a bar”. That is a measurement
of history, and reading the later bar is exactly what measuring it means. The same line inside a
trading rule would be look-ahead bias — a formula deciding today’s action using tomorrow’s
price — and it is the single most common way backtests are made to look brilliant. Part 30
gives it a whole lesson. The rule to carry away: a positive Ref() shift is acceptable in a
measurement and never acceptable in a signal.
Two more design points, both handled in the code.
Nulls must be excluded explicitly. RSI( 14 ) is Null on bars 0 to 13, and the last 20
bars have no future close. AmiBroker’s documentation is clear that a comparison against Null
yields Null rather than false, and that those Nulls propagate — so Cum() over an array
containing them does not count what you would expect. The formulas below build an explicit
0-or-1 Measurable flag with IsNull() and multiply by it, which makes the exclusion visible
instead of accidental.
Record the sample size next to every average. An average of 14 observations and an average of 1,400 are different kinds of object. Any test that reports a mean without its count is withholding the most important number.
The test, on one instrument
Section titled “The test, on one instrument”Start here, because you can see what is happening.
Complete runnable AFL
// rsi-above-70-chart-test.afl// Part 6 - Reality Check: Does RSI Above 70 Mean Sell?//// Measures what actually happened AFTER RSI closed above a chosen level on the// symbol in front of you, and compares it with what happened after every other// measurable bar of the same history.//// READ THIS BEFORE COPYING ANYTHING:// Ref( Close, Horizon ) with a POSITIVE shift reads a FUTURE bar. That is// legitimate here because we are measuring something that has already// happened. It is a fatal bug in a trading rule. Nothing in this formula// may be pasted into a system.//// Assumptions declared up front:// - Daily bars, split- and dividend-adjusted. Unadjusted history produces// fictional forward returns on every adjustment date.// - RSI( 14 ) is Null for bars 0..13, and the last Horizon bars have no// future to look at. Both groups are excluded from every count below.// - The result describes this symbol over this history. It is one sample.
_SECTION_BEGIN( "RSI level test" );
RsiPeriod = Param( "RSI period", 14, 2, 50, 1 );RsiLevel = Param( "RSI level", 70, 50, 95, 1 );Horizon = Param( "Forward horizon (bars)", 20, 1, 120, 1 );
RsiLine = RSI( RsiPeriod );
// Percentage change from this bar's close to the close Horizon bars later.ForwardReturn = 100 * ( Ref( Close, Horizon ) - Close ) / Close;
// A bar can be measured only if BOTH the indicator and the future close exist.// IsNull() is used explicitly because a Null propagates through every// comparison it touches: "Null > 70" is Null, not False, and Cum() of an array// containing Nulls does not count what you think it counts.Measurable = IIf( IsNull( ForwardReturn ) OR IsNull( RsiLine ), 0, 1 );SafeReturn = Nz( ForwardReturn );SafeRsi = Nz( RsiLine );
Triggered = IIf( Measurable AND SafeRsi > RsiLevel, 1, 0 );WinFlag = IIf( SafeReturn > 0, 1, 0 );
// Running totals. Multiplying by the 0/1 flag is the array way of saying// "add this bar only when the condition held".HitCount = Cum( Triggered );HitSum = Cum( Triggered * SafeReturn );HitWins = Cum( Triggered * WinFlag );
AllCount = Cum( Measurable );AllSum = Cum( Measurable * SafeReturn );AllWins = Cum( Measurable * WinFlag );
HitMean = IIf( HitCount > 0, HitSum / HitCount, 0 );AllMean = IIf( AllCount > 0, AllSum / AllCount, 0 );HitRate = IIf( HitCount > 0, 100 * HitWins / HitCount, 0 );AllRate = IIf( AllCount > 0, 100 * AllWins / AllCount, 0 );
Plot( RsiLine, "RSI(" + RsiPeriod + ")", colorBlue, styleLine | styleThick );PlotGrid( RsiLevel, colorRed, 10, 2, True );PlotGrid( 100 - RsiLevel, colorGreen, 10, 2, True );
// The running means are the most useful thing on this chart. They show how the// answer wandered as history accumulated - which is how you find out whether// the final number is a measurement or an accident of where the data stops.Plot( HitMean, "Running mean after the level (%)", colorRed, styleLine | styleOwnScale | styleNoLabel, -25, 25 );Plot( AllMean, "Running mean, all bars (%)", colorGrey40, styleLine | styleOwnScale | styleNoLabel, -25, 25 );
Title = Name() + " RSI(" + RsiPeriod + ") above " + RsiLevel + ", " + Horizon + "-bar forward return\n" + "After the level: " + NumToStr( LastValue( HitCount ), 1.0 ) + " bars" + " mean " + NumToStr( LastValue( HitMean ), 1.2 ) + "%" + " positive " + NumToStr( LastValue( HitRate ), 1.1 ) + "%\n" + "All measured bars: " + NumToStr( LastValue( AllCount ), 1.0 ) + " bars" + " mean " + NumToStr( LastValue( AllMean ), 1.2 ) + "%" + " positive " + NumToStr( LastValue( AllRate ), 1.1 ) + "%";
_SECTION_END();Paste it into Analysis -> Formula Editor, name it, and press Apply Indicator on an empty
pane. You will get RSI with its level lines, two running-mean lines drawn on their own scale,
and a two-line readout in the title: the number of qualifying bars, their mean forward return
and the share of them that were positive, above the same three figures for every measurable bar.
The running means are the point of this version. They show how the answer evolved as history accumulated. If the red line and the grey line have been drifting towards each other for a decade, the current gap between them is mostly a story about the early data. If the red line has wandered from strongly negative to strongly positive and back, then the single number in the title is a snapshot of a quantity that does not sit still, and quoting it without that context would be misleading.
The test, across a universe
Section titled “The test, across a universe”One symbol is an anecdote. This version asks the same question of every symbol in a watch list and gives you one row each.
Complete runnable AFL
// rsi-above-70-exploration.afl// Part 6 - Reality Check: Does RSI Above 70 Mean Sell?//// The same measurement as the chart version, but one row per symbol, so the// question is asked of a whole universe instead of a single instrument.//// How to run it:// Formula Editor -> paste -> give it a name -> Send to Analysis// Apply to: Filter, and choose your watch list// Range: All quotations// Then press Explore.//// READ THIS BEFORE COPYING ANYTHING:// ForwardReturn reads a FUTURE bar on purpose. It measures what already// happened. It is not a rule, and it cannot be traded.//// Assumptions declared up front:// - Daily bars, split- and dividend-adjusted.// - The universe you point this at is the universe the answer applies to.// A watch list of today's index members answers a question about// survivors, which is a different question from the one you asked.// - Symbols with too little history, or too few qualifying bars, are dropped// rather than reported with a meaningless average. See MinBars / MinHits.
RsiPeriod = 14;RsiLevel = 70;Horizon = 20;MinBars = 250; // usable bars a symbol must have before it is reportedMinHits = 30; // qualifying bars a symbol must have before it is reported
RsiLine = RSI( RsiPeriod );ForwardReturn = 100 * ( Ref( Close, Horizon ) - Close ) / Close;
Measurable = IIf( IsNull( ForwardReturn ) OR IsNull( RsiLine ), 0, 1 );SafeReturn = Nz( ForwardReturn );SafeRsi = Nz( RsiLine );
Triggered = IIf( Measurable AND SafeRsi > RsiLevel, 1, 0 );WinFlag = IIf( SafeReturn > 0, 1, 0 );
HitCount = Cum( Triggered );HitSum = Cum( Triggered * SafeReturn );HitWins = Cum( Triggered * WinFlag );
AllCount = Cum( Measurable );AllSum = Cum( Measurable * SafeReturn );AllWins = Cum( Measurable * WinFlag );
HitMean = IIf( HitCount > 0, HitSum / HitCount, 0 );AllMean = IIf( AllCount > 0, AllSum / AllCount, 0 );HitRate = IIf( HitCount > 0, 100 * HitWins / HitCount, 0 );AllRate = IIf( AllCount > 0, 100 * AllWins / AllCount, 0 );
Difference = HitMean - AllMean;
// One row per symbol: only the last bar of the analysis range is accepted, and// by that bar the running totals hold the whole history.Filter = Status( "lastbarinrange" ) AND AllCount >= MinBars AND HitCount >= MinHits;
AddColumn( AllCount, "Measured bars", 1.0 );AddColumn( HitCount, "Bars above level", 1.0 );AddColumn( HitMean, "Mean fwd % after", 1.2 );AddColumn( AllMean, "Mean fwd % all bars", 1.2 );AddColumn( Difference, "Difference", 1.2 );AddColumn( HitRate, "% positive after", 1.1 );AddColumn( AllRate, "% positive all bars", 1.1 );
// Average and count rows across symbols. Read the average row carefully: it// gives every symbol equal weight, however many qualifying bars it contributed.AddSummaryRows( 2 | 16, 1.2 );To run it: paste it into the Formula Editor, name it, and press Send to Analysis. In the
Analysis window set Apply to to Filter and choose your watch list, set Range to
All quotations, and press Explore. You will get a table with one row per symbol and, at
the bottom, an average row and a count row produced by AddSummaryRows.
Three details in that formula are worth understanding rather than copying.
Filter = Status( "lastbarinrange" ) AND ... accepts only the final bar of the analysis range
for each symbol. By that bar the Cum() totals hold the whole history, so one row per symbol
carries the complete result. Status( "lastbarinrange" ) is the documented way to ask for that
bar.
MinBars and MinHits drop symbols with too little history or too few qualifying bars. This is
a deliberate, declared choice: an average over eight observations is noise, and including it
would corrupt the summary row. It also has a cost — you are excluding exactly the symbols where
the condition was rare, which may not be a random group. Declaring the rule in the code, where
it is visible, is the honest way to make such a choice.
AddSummaryRows( 2 | 16, 1.2 ) adds an AVERAGE row and a COUNT row. Read that average
carefully: it averages symbols, not observations. A symbol contributing 40 qualifying bars
counts exactly as much as one contributing 900. That is one defensible way to aggregate — it
stops a single heavily-qualifying symbol dominating — but it is not the same as pooling every
observation, and the two can disagree. Say which one you used.
How to read the outcome
Section titled “How to read the outcome”You now have a table. Here is how to interpret each shape it might take, and what to check before believing any of them.
The difference is close to zero. The most likely single outcome, and the most informative one. It means that on this universe, over this period, at this horizon, knowing that RSI was above 70 told you nothing about the following twenty bars that you did not already know from the instrument’s general behaviour. The claim has not been refuted for all time; it has failed to show up here.
The difference is negative — lower returns after high RSI. Consistent with the claim as stated. Before accepting it, check every item in the next section. In particular, check whether the effect is driven by a handful of symbols, or by one period such as a single bear market that everything went down in.
The difference is positive — higher returns after high RSI. Also a common outcome, and it is what a momentum-continuation reading of the same data would predict. Note that this is evidence against the popular claim as stated, which is a real finding and worth recording. It is not evidence for the opposite rule, because “buy after RSI above 70” would need its own test, with its own costs and its own baseline.
A large difference on a small count. Treat with the most suspicion of all. Sort the table by the count column and look at the extreme rows. Large effects on small samples are what random variation looks like.
Huge variation between symbols. Very common, and the most useful thing the exploration gives you that the single-symbol version cannot. If half the symbols show a positive difference and half negative, the summary row’s average is a number that describes none of them.
Checks to run before you believe any of it
Section titled “Checks to run before you believe any of it”- Sample size. How many qualifying bars per symbol? How many symbols survived the filters?
- Dispersion. Is the sign of the difference consistent across symbols, or is the average the residue of two opposing groups?
- Stability over time. Look at the running means on the single-symbol chart. Has the estimate been stable, or is the current value a recent accident?
- Sensitivity to the free parameters. Re-run with RSI periods of 7 and 21, with levels of 65, 70 and 80, and with horizons of 5, 20 and 60. If the answer flips between neighbouring settings, you have found noise. If it changes smoothly and keeps its sign, that is much more interesting — and it is the same reasoning Part 31 applies to optimisation results.
- The mean against the hit rate. The average and the percentage-positive columns can point different ways. A positive average with a below-half hit rate means a few large winners are carrying it, which is a completely different market description from many small ones.
- Overlap. With daily bars and a 20-bar horizon, consecutive observations share 19 of their 20 days. Hundreds of “independent” rows are nothing of the kind, so the effective sample is far smaller than the count suggests, and ordinary significance calculations do not apply to it. This alone should stop you treating a small difference as established.
Why the answer is not universal
Section titled “Why the answer is not universal”Whatever you found, it is not the answer to “does RSI above 70 mean sell”. It is the answer to a much narrower question, and the narrowing happened in at least six places.
The universe. Different markets, sizes, sectors and countries behave differently, and your list is one draw from an enormous space of possible lists.
The period. Market character changes. A test spanning one long advance and a test spanning a choppy decade can disagree completely, and both can be correctly computed.
The interval. Everything here is daily. RSI on weekly bars is a different measurement with different saturation behaviour and different sample sizes.
The parameters. 14 and 70 and 20 were choices. Six free parameters were fixed by convention before the test began.
The measurement. Close-to-close percentage change ignores the path. Two bars with identical 20-day returns can have offered completely different experiences in between, and a rule with a stop would have treated them differently.
The absence of a strategy. The test measures price changes, not returns to a trader. There are no costs, no spread, no slippage, no position sizing, no shorting constraints and no borrowing fees. “Sell” in the original claim might mean exiting a long or opening a short, and those have different mechanics, different risks and different costs.
Limitations of the test itself
Section titled “Limitations of the test itself”Separate from the narrowness of the question, the instrument you just built has its own defects. Knowing them is part of owning the result.
- The baseline includes the conditional bars.
AllCountcounts every measurable bar, including the qualifying ones, so any real difference is understated slightly. If you would rather compare against the complement, change the baseline flag toMeasurable AND NOT Triggeredand re-run. Report which one you used. - Symbol-weighted, not observation-weighted. As described above. The pooled average is a different number and needs a different formula.
- The mean is fragile. A single 300 per cent move in one symbol can move a mean built from hundreds of observations. A median would be more robust, and computing a conditional median across bars needs machinery this formula does not have.
- Overlapping windows. Discussed above, and it is the most serious statistical problem here.
- Warm-up is handled crudely. Excluding
Nullbars is not the same as excluding seed-contaminated bars. RSI’s recursion stays sensitive to its starting values well past bar 14, so the earliest measurable bars of each symbol are lower quality than the rest. - Survivorship, if your watch list came from a current index. Already noted, still the largest single threat.
- No transaction costs, ever. The test cannot say anything about whether a rule based on the condition would have been worth trading.
- You may run it many times. Six free parameters and a universe you can edit is a lot of opportunity to keep going until something looks good. Decide your settings and your universe before you look, and count every variant you try. Part 30 names this data snooping and shows how quickly it manufactures results.
What to do with the answer
Section titled “What to do with the answer”Write it down, with all six specifications and the checks you ran, in whatever notebook you are going to keep for the rest of this course. Three sentences is enough. The value is not in the number; it is in having a record of a question you asked precisely, so that in Part 32 you can ask it again on data you had not looked at yet.
Then go and find the next confident sentence in whatever you were reading before this course, and do the same thing to it. That habit — specify, measure, compare against a baseline, list the limits — is the entire method. Everything after this page is that method with better tools.
“RSI above 70 means sell” cannot be evaluated as stated, because it names no population, no
period, no horizon, no alternative and no measure. RSI above 70 is the arithmetic signature of a
market that has been rising consistently, so the claim reduces to advice about selling strength
rather than to a discovery about exhaustion. Specifying it requires a universe, a period, a
condition, a measurement, a comparison and a decision rule fixed in advance. The forward-looking
Ref() shift used to measure it is legitimate in a measurement and fatal in a signal. And
whatever number comes out, it is a statement about your symbols, your period and your six
parameter choices — reported next to its baseline, its sample size and its list of limits, or
not reported at all.
Check your understanding
Sources for this lesson
8 verified · checked 2026-08-31
- 01AFL Function Reference — RSI§ Internal implementation comment by Tomasz Janeczkoamibroker.com/guide/afl/rsi.html2026-08-31
- 02AFL Function Reference — Refamibroker.com/guide/afl/ref.html2026-08-31
- 03AFL Function Reference — Cumamibroker.com/guide/afl/cum.html2026-08-31
- 04AFL Function Reference — Nzamibroker.com/guide/afl/nz.html2026-08-31
- 05AFL Function Reference — Status§ firstbarinrange and lastbarinrangeamibroker.com/guide/afl/status.html2026-08-31
- 06AFL Function Reference — AddSummaryRowsamibroker.com/guide/afl/addsummaryrows.html2026-08-31
- 07AmiBroker User's Guide — New Analysis windowamibroker.com/guide/h_newanalysis.html2026-08-31
- 08AmiBroker User's Guide — Understanding how AFL language worksamibroker.com/guide/h_understandafl.html2026-08-31
Every technical claim on this page was checked against the official AmiBroker documentation on the date shown. Where the course disagrees with folklore, the source is how you can tell which one to trust.