Skip to content
Level 1 · Chart ReaderLessonPart 06 · page 6 of 1128 min
28Minutes
4AFL functions
6Sources
StandardRequires
AFL functions taught here4

RSI and Rate of Change

RSI is the most widely used and most widely misread indicator in technical analysis. The misreading is not a matter of opinion — it follows from the published formula, which AmiBroker prints in full on its own reference page. This lesson works through that formula until the behaviour everyone complains about becomes obviously inevitable, then does the same for the simpler indicator that sits next to it, rate of change.

AmiBroker publishes the internal algorithm. Here it is as structured English:

Pseudocode — not valid AFL

P = 0 // smoothed average up-move
N = 0 // smoothed average down-move
for each bar i after the first:
diff = Close[i] - Close[i-1]
W = diff if diff > 0, else 0
S = -diff if diff < 0, else 0
P = ( (period - 1) * P + W ) / period
N = ( (period - 1) * N + S ) / period
if i >= period:
result[i] = 100 * P / ( P + N )

Read it as three statements.

One: RSI sees only the differences between consecutive closes. Not the highs, not the lows, not the ranges, not the gaps, not the volume. A day that opened limit-down, traded across a 9 per cent range and closed 0.1 per cent up is, to RSI, a small up-move.

Two: the smoothing is Wilder’s, and it is recursive. Each new P is a blend of the previous P and today’s up-move, weighted (period-1) to 1. That is not a simple average of the last fourteen gains, and it is not the standard 2/(n+1) exponential average either. This is why RSI values from two platforms can differ slightly, and it is why P and N never fully forget: every bar the indicator has seen still contributes a decaying share.

Three: the result is a ratio of a non-negative number to a sum of non-negative numbers, so it is confined to the range 0 to 100 by arithmetic, not by clipping.

Unchanged closes do not move RSI at all. If today’s close equals yesterday’s, both W and S are zero, so both P and N are multiplied by (period-1)/period. The ratio P / ( P + N ) is unchanged. A week of identical closes leaves RSI exactly where it was — which matters on illiquid instruments where flat closes are common, because the indicator looks stable when in fact nothing is being measured.

A persistent advance drives RSI towards 100 mechanically. Suppose ten consecutive up closes. On each of them S is zero, so N is multiplied by 13/14 each time. After ten such bars N has shrunk to about 48 per cent of where it started, and after twenty to about 23 per cent, while P is being topped up on every bar. 100 * P / ( P + N ) climbs towards 100 because the denominator is collapsing.

What a run of up closes does to the ratio

With period 14, each bar that has no down-move multiplies N by 13/14. The RSI row assumes P is merely maintained, not increased — so even a slow, steady grind upward pushes the reading into the seventies. Nothing about the market has been evaluated; this is the decay of a denominator.
Barstart+5 bars+10 bars+15 bars+20 bars
N, as a share of its starting value1.000.690.480.330.23
RSI if P holds steady50.059.267.775.381.5
With period 14, each bar that has no down-move multiplies N by 13/14. The RSI row assumes P is merely maintained, not increased — so even a slow, steady grind upward pushes the reading into the seventies. Nothing about the market has been evaluated; this is the decay of a denominator.

That table is the entire answer to “why does RSI stay above 70 for months in a strong trend”. It is not the indicator failing to warn you. A high reading is the signature of a market that has been going one way with few interruptions, which is the definition of a trend.

It does not measure how far price has moved. A stock that has climbed 3 per cent in fourteen sessions without a single down day will read higher than one that has climbed 30 per cent in fourteen sessions with five sharp pullbacks. RSI measures the consistency of direction, weighted by the size of the moves — a different thing from the size of the move itself.

It does not measure value, cheapness, or extension from any average. It has no idea where price is relative to anything.

And its level is not comparable in any absolute sense between instruments with different character. A steady index and a speculative small-cap will spend their time in different parts of the 0-to-100 range as a matter of habit.

Where RSI is a smoothed ratio, ROC is a single subtraction. AmiBroker publishes both forms:

Fragment — not a complete formula

// The documented definition of ROC, for reference — not something to type.
// absmode = False (the default):
100 * ( array - Ref( array, -periods ) ) / Ref( array, -periods )
// absmode = True:
100 * ( array - Ref( array, -periods ) ) / abs( Ref( array, -periods ) )

Four documented details, each of which catches people:

The default period is 12, not 14 and not 10. ROC( Close ) is a twelve-bar rate of change. It is the one momentum function in this family whose default is not 14.

The result is already a percentage. It is multiplied by 100 inside the function. Do not multiply again.

The array argument is required and comes first. Unlike RSI, MFI, CCI and ADX, ROC does not default to the close — ROC( 12 ) is an error.

absmode exists for arrays that can go negative. With the default False the denominator is the signed earlier value, so applying ROC to an oscillator, a spread or the MACD line flips the sign of the result whenever that earlier value was negative.

The behavioural difference from RSI matters more than the arithmetic. Because ROC compares exactly two bars and ignores everything in between, it is jumpy, and it has a drop-out artefact of its own: on the bar where a large move falls out of the comparison window, the reading changes sharply even though today did nothing. If you see a momentum reading collapse on a quiet day, look twelve bars back before looking for news.

ROC has one real advantage over almost everything else in this part: it is a percentage, so it is directly comparable between instruments. That property is what makes it the natural building block for relative strength ranking in Part 13.

RSI belongs in its own pane. Drop it there from Window -> Charts by double-clicking. If you drag it onto the price pane instead you get a flat line along the bottom of the chart, because the pane is scaled for prices; the documented fix is the styleOwnScale style from Parameters -> Style, though a separate pane is usually what you actually wanted.

For the horizontal lines, the Parameters window’s Axes & Grid tab offers a fixed set of popular grid levels including 30/70, 20/80 and 10/90, which covers the usual choices without any code. From a formula, PlotGrid is the documented way to draw a constant level — AmiBroker’s own page recommends it over plotting a constant array, for performance:

Fragment — not a complete formula

Momentum = RSI( 14 );
Plot( Momentum, "RSI(14)", colorBlue, styleLine | styleThick );
PlotGrid( 70, colorRed, 9, 1, True );
PlotGrid( 30, colorGreen, 9, 1, True );
PlotGrid( 50, colorGrey40, 8, 1, False );

Draw the 50 line as well as the 70 and 30 lines. It is the level at which smoothed up-moves and down-moves are equal, which is a more meaningful reference point than either of the others, and almost nobody plots it.

ROC is unbounded, so it also needs its own pane, with a grid line at zero:

Fragment — not a complete formula

ChangePct = ROC( Close, 12 ); // already a percentage
Plot( ChangePct, "ROC(Close, 12) %", colorBlue, styleLine | styleThick );
PlotGrid( 0, colorBlack, 9, 1, False );

Fragment — not a complete formula

Momentum = RSI( 14 ); // no input array — RSI reads the built-in Close
ChangePct = ROC( Close, 12 ); // ROC requires the array, and it comes first
Extended = Momentum > 70; // a state, true on every qualifying bar
JustCrossed = Cross( Momentum, 70 ); // an event, true on one bar

Two naming traps. rsi is a function name, so rsi = RSI( 14 ); does not compile — AmiBroker’s language reference states that user identifiers cannot duplicate function names. And if you need RSI of something other than the close, the documented function is RSIa( array, periods = 14 ), with the array first: RSIa( High, 12 ) is AmiBroker’s own example. Searching the on-line function index for it finds nothing, because it is documented as a second syntax on the RSI page rather than as its own entry. It is real; it is just filed oddly, exactly like CCIa on the CCI page.

Here is the claim, stated the way it is usually stated:

RSI above 70 means the market is overbought. Sell.

Everything in this lesson bears on it. RSI above 70 means the smoothed average up-move has been comfortably larger than the smoothed average down-move over a recursive window — which is to say, the market has been rising steadily. The advice therefore reduces to “sell markets that have been rising steadily”, which may be good advice or terrible advice, but is certainly not something the indicator discovered.

Notice also what the claim leaves unspecified: which market, over what period, sold at what price, held for how long, compared against what alternative. Without those, it cannot be wrong, and a statement that cannot be wrong is not knowledge.

The final lesson of this part takes that sentence, turns it into something that can be wrong, tests it on your own data, and reads the result honestly — including a frank list of what the test settles and what it does not.

RSI is 100 * P / ( P + N ), where P and N are Wilder-smoothed averages of up-moves and down-moves of the close. It sees only close-to-close differences, it is bounded by construction, it is Null for bars 0 to periods - 1, and it is unaffected by unchanged closes. A persistent one-way move collapses the denominator and drives the reading towards its ceiling, so a high value is the signature of a trend rather than a warning about one. ROC is the two-point alternative: already a percentage, default period 12, array argument required and first, with an absmode argument for inputs that can go negative. Both are momentum measurements. Neither is an instruction.

Check your understanding

Question 1. A share closes unchanged for six consecutive sessions. What happens to RSI(14)?
Show the answer and why

Answer: It stays where it was

With diff = 0 both W and S are zero, so P and N are each multiplied by 13/14. The ratio P / ( P + N ) is unchanged. This is worth knowing on illiquid instruments, where a flat RSI can mean "nothing measured" rather than "balanced market".

Question 2. Which call correctly computes a 20-bar rate of change of volume?
Show the answer and why

Answer: ROC( Volume, 20 )

ROC requires the array and it comes first: roc( ARRAY, periods = 12, absmode = False ). Unlike RSI or ADX it has no implicit input, and its default period is 12 rather than 14.

Question 3. RSI(14) has been above 70 for eleven consecutive weeks on a rising instrument. What does that tell you?
Show the answer and why

Answer: That the advance has had few and small down-closes, which is what pushes the ratio towards its ceiling

A sustained one-way move shrinks N on every bar that has no down-move, so the ratio climbs by arithmetic. Sustained high readings are the expected behaviour of the formula during a trend, not a signal about what comes next.

Question 4. Which of these are documented facts about AmiBroker’s RSI and ROC? Select all that apply.
Show the answer and why

Answer: RSI( 14 ) returns Null on bars 0 through 13, ROC multiplies its result by 100 internally, ROC’s absmode argument divides by the absolute value of the earlier element

RSI takes only a period; the variant that accepts an array is RSIa, documented as a second syntax on the same page with the array first. The other three come straight from the published formulas.

Sources for this lesson

6 verified · checked 2026-08-31

  1. 01AFL Function Reference — RSI§ Internal implementation comment by Tomasz Janeczkoamibroker.com/guide/afl/rsi.html2026-08-31
  2. 02AFL Function Reference — ROCamibroker.com/guide/afl/roc.html2026-08-31
  3. 03AFL Function Reference — Refamibroker.com/guide/afl/ref.html2026-08-31
  4. 04AFL Function Reference — PlotGridamibroker.com/guide/afl/plotgrid.html2026-08-31
  5. 05AmiBroker User's Guide — Parameters window§ Grid levelsamibroker.com/guide/w_param.html2026-08-31
  6. 06AmiBroker User's Guide — AFL language reference§ Identifiersamibroker.com/guide/a_language.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.