Skip to content
Level 3 · AFL DeveloperLessonPart 13 · page 1 of 526 min
26Minutes
5AFL functions
6Sources
StandardRequires
AFL functions taught here5

Relative Strength: What It Does and Does Not Mean

Two analysts look at the same stock on the same day. One says it has been strong for months. The other says it has been weak all year. Both are right, and neither is being careless: they are measuring against different things over different windows, and “relative strength” is a family of measurements rather than a single number.

By the end of this lesson you should be able to say, for any relative strength figure you meet, three things: what the numerator is, what the denominator is, and over what window it was measured. If you cannot answer all three, the number is not yet a measurement.

AmiBroker’s RSI() is documented as the relative strength index. It compares a symbol to its own recent history — the size of its up moves against the size of its down moves. It never looks at another symbol. It is a single-symbol, time-series indicator, and it has nothing to do with the subject of this part.

What this part is about is comparative relative strength: one symbol measured against another symbol, or against an index, on the same bars. AmiBroker keeps that under a separate function, RelStrength(), filed in the reference under “Referencing other symbol data”. The two share four letters and no concept.

The oldest form is a plain ratio: divide one price series by another, bar by bar.

Fragment — not a complete formula

Ratio = Close / Foreign( "^GSPC", "C" );

That is one number per bar, like every other AFL expression. Here is what it looks like on five bars where the stock rises 20 per cent and the index rises 10 per cent:

A relative strength ratio is computed bar by bar

The ratio's level is an accident of the two price scales. Only its change carries information.
Bar12345
Close (stock)50.051.053.054.060.0
Foreign index close10001010103010401100
Ratio0.05000.05050.05150.05190.0545
The ratio's level is an accident of the two price scales. Only its change carries information.

The important property is in the caption. A ratio of 0.05 means nothing on its own — it would be 50 if the index were quoted in thousands, or 0.00005 if the stock had done a 1-for-1000 split. What is readable is the change in the ratio: a rising line says the numerator outpaced the denominator over that stretch; a falling line says the reverse.

Two consequences follow immediately, and both catch people out.

A rising ratio does not mean a rising price. If the stock falls 10 per cent while the index falls 25 per cent, the ratio rises. “Outperforming” and “going up” are different statements, and a screen built on a rising ratio will happily hand you a list of things that are all falling.

A ratio line has no fixed zero. Comparing the ratio’s level across two symbols is meaningless, because each pair has its own arbitrary scale. This is why ratio charts are usually rebased — set to 100 at a chosen start date — before two of them are put side by side, and why the choice of start date then quietly becomes part of the answer. Part 15 takes rebasing and its start-date sensitivity apart in detail.

RelStrength( "tickername", fixup = 1 ) returns an array described on its official page as the comparative relative strength of the currently selected security against tickername. The page documents the arguments and the hole-filling behaviour, but it does not print the arithmetic. If you need to know exactly which number you are looking at — and for anything you intend to rank on, you do — compute the ratio yourself so that the definition is in your formula rather than in a footnote you cannot read.

Choosing the denominator changes the question

Section titled “Choosing the denominator changes the question”

The denominator is not a technicality. It decides what question you are asking.

Against a broad benchmark, you are asking whether the symbol beat the market. That suits a decision about whether to hold this instead of an index fund, and it is the comparison most published “relative strength” numbers use.

Against a sector, you are asking whether the symbol beat its peers. This separates two effects that a market comparison merges: a mining stock in a year when mining did well may look excellent against the broad market and unremarkable against other miners. Which of those you want depends entirely on the decision in front of you. Note that “the sector” has to be represented by something concrete — a sector index, a sector ETF, or a composite you build yourself — and each of those has its own defects, which Part 15 and Part 16 cover.

Against a single peer, you are asking a pair question, which is the basis of spread and pairs research. It is the most specific and the least stable: two-symbol relationships come apart without warning.

Momentum ranking measures something else again

Section titled “Momentum ranking measures something else again”

The other common meaning of “relative strength” — the one that drives most published ranking systems — is not a ratio line at all. It is a return over a fixed window, computed for every symbol, and then compared across symbols on the same bar. Sometimes the benchmark’s return over the same window is subtracted first, giving an excess return.

Fragment — not a complete formula

ExcessReturn = ROC( Close, 126 ) - ROC( BenchClose, 126 );

The distinction matters because the two answer different questions. The ratio line is a path: it shows when the symbol was gaining ground and when it was losing it. The window return is a single summary of the whole window that says nothing about the path taken to get there. A stock that rose steadily for six months and one that collapsed and then doubled can have identical six-month returns and completely different ratio lines.

Why “strongest” is a statement about a window

Section titled “Why “strongest” is a statement about a window”

This is the point worth taking away from the lesson, and it is easier to see than to argue about.

Build a chart pane that measures one symbol’s excess return over a benchmark across three different lookback windows at the same time, so you can watch the three disagree.

Complete runnable AFL

relative-strength-lookbacks.afl
// relative-strength-lookbacks.afl
// Part 13 - Relative Strength: What It Does and Does Not Mean
//
// Measures one symbol's relative strength against one benchmark over three
// different lookback windows at once, so that the disagreement between the
// windows is visible instead of assumed.
//
// Assumptions declared up front:
// - Daily bars, and a benchmark symbol that exists in this database. The
// formula checks that with the documented return value of SetForeign
// rather than guessing, because a missing benchmark produces a chart that
// looks fine and means nothing.
// - Switching to the benchmark with SetForeign uses the documented default
// fixup = 1: benchmark days that are missing are filled from the previous
// close and carry zero volume. Days the CURRENT symbol did not trade do
// not exist for either series.
// - The first LongLookback bars are warm-up and carry no information.
// - Nothing here is a signal. It is a description of past relative price.
_SECTION_BEGIN( "Relative strength lookbacks" );
BenchSymbol = ParamStr( "Benchmark symbol", "^GSPC" );
ShortLookback = Param( "Short lookback (bars)", 21, 5, 63, 1 );
MidLookback = Param( "Medium lookback (bars)", 63, 20, 150, 1 );
LongLookback = Param( "Long lookback (bars)", 252, 60, 500, 1 );
// SetForeign returns 1 when the ticker exists and 0 when it does not, and on
// failure it leaves the price arrays untouched. That documented return value
// is the only reliable existence test available to us.
BenchExists = SetForeign( BenchSymbol );
BenchClose = Close; // while the swap is in force, Close IS the benchmark
RestorePriceArrays();
// Excess return over each window: how far the symbol's own percentage change
// over exactly those bars exceeded the benchmark's, in percentage points.
// Subtracting two rates of change is not identical to the rate of change of
// the ratio, but it is easier to read and far easier to check by hand.
ShortExcess = ROC( Close, ShortLookback ) - ROC( BenchClose, ShortLookback );
MidExcess = ROC( Close, MidLookback ) - ROC( BenchClose, MidLookback );
LongExcess = ROC( Close, LongLookback ) - ROC( BenchClose, LongLookback );
// If the benchmark is missing, BenchClose is this symbol's own close, so every
// excess reading collapses to zero. Blanking the plots makes that obvious.
ShortExcess = IIf( BenchExists, ShortExcess, Null );
MidExcess = IIf( BenchExists, MidExcess, Null );
LongExcess = IIf( BenchExists, LongExcess, Null );
Plot( ShortExcess, "Excess % over " + ShortLookback + " bars", colorBlue, styleLine );
Plot( MidExcess, "Excess % over " + MidLookback + " bars", colorOrange, styleLine );
Plot( LongExcess, "Excess % over " + LongLookback + " bars", colorGreen, styleLine | styleThick );
// Zero is the only level on this pane with a meaning: above it the symbol
// outpaced the benchmark over that window, below it the benchmark won.
PlotGrid( 0, colorGrey40 );
Title = Name() + " relative to " + BenchSymbol + " " +
WriteIf( BenchExists, "",
"BENCHMARK NOT FOUND - nothing on this pane means anything. " ) +
"excess over " +
NumToStr( ShortLookback, 1.0 ) + "/" +
NumToStr( MidLookback, 1.0 ) + "/" +
NumToStr( LongLookback, 1.0 ) + " bars: " +
WriteVal( ShortExcess, 1.1 ) + "% " +
WriteVal( MidExcess, 1.1 ) + "% " +
WriteVal( LongExcess, 1.1 ) + "%";
_SECTION_END();

Download relative-strength-lookbacks.afl67 lines

The formula has three logical sections. The first reads the benchmark. Rather than guessing whether the benchmark symbol exists, it uses SetForeign(), whose official page states that it returns True when the ticker exists and False otherwise, and that on failure the price arrays are left unchanged. That gives an exact existence test and, in the same call, swaps every price array over to the benchmark so the plain identifier Close refers to the benchmark’s closes until RestorePriceArrays() puts things back.

The second section computes three excess returns, one per lookback, by subtracting the benchmark’s rate of change from the symbol’s over exactly the same number of bars. The third blanks all three series with Null when the benchmark was not found, because a missing benchmark would otherwise leave BenchClose holding this symbol’s own closes and every excess reading would collapse to a tidy, meaningless zero.

  • SetForeign( ticker, fixup = True, tradeprices = False ) — swaps the built-in price arrays to another symbol. Note that the second argument is fixup, not a data field: Foreign() takes the field second, SetForeign() does not, and mixing them up is a silent logic error rather than a syntax error.
  • RestorePriceArrays( tradeprices = False ) — puts the original arrays back. It is mandatory after every SetForeign(), and its tradeprices argument must match.
  • ROC( ARRAY, periods = 12, absmode = False ) — percentage rate of change over periods bars.
  • PlotGrid( level, color ) — draws the horizontal line at zero, which is the only level on this pane that means anything.

Three lines oscillating around zero and a grid line at zero. On a liquid share with several years of daily history, the short line will cross zero many times a year, the medium line rather less, and the long line may spend a year at a time on one side. That picture is the lesson: at most bars the three lines are not on the same side of zero.

Pick a bar where the short line is well above zero and the long line well below it. Read the symbol’s own close on that bar and the close 21 bars earlier, and do the percentage change by hand. Do the same for the benchmark, subtract, and check it matches the blue line’s value in the chart title. Then repeat with 252 bars. Two arithmetic checks are enough to convince you the numbers are what they claim to be — and that the disagreement is real rather than a plotting artefact.

  • A flat line at exactly zero. The benchmark symbol was not found, and the title says so. Check the ticker’s spelling against Symbol → Information.
  • Wild values at the left edge of the chart. The first bars are warm-up; ROC() over 252 bars is undefined until there are 252 prior bars.
  • Every symbol looks strong. Check what the benchmark actually is. A benchmark that is itself a weak sector index will make almost everything look good.
  • Values that jump on one date across many symbols. That is usually a data problem in the benchmark, not a market event. Chart the benchmark on its own.

Add a fourth line: the excess return over the shortest window, but computed against a sector proxy rather than the index. Watching a symbol be strong against its sector and weak against the market — or the reverse — is the most useful version of this picture.

Beyond that, a relative strength reading is silent on several things that decide whether acting on it is possible at all:

  • Liquidity. The strongest name in a universe is often the smallest and thinnest one. Nothing in a ratio or a return knows about spread, depth or the size you could trade.
  • Risk. Two symbols with identical excess returns can have had entirely different drawdowns getting there. Rank on return alone and you rank volatility in through the back door.
  • The reason. A takeover bid, a one-off contract award and a genuine change in trading fortunes look the same in a rate of change.
  • The universe. A ranking computed over today’s index members tells you about a set chosen with hindsight. Part 30 has the full account of why that matters.
  • What happens next. The measurement is entirely of the past. It has no forward component in it at all.

Relative strength is not one thing. It is a ratio, or an excess return, measured against a denominator you chose, over a window you chose. The ratio’s level is arbitrary and only its change is readable; the window return is a summary that discards the path. Changing the benchmark changes the question, and changing the window changes the answer — often completely. The next lesson takes the second half of that sentence seriously and asks what happens when you stop testing symbols one at a time and start putting them in order.

Check your understanding

Question 1. A stock falls 8% while its benchmark falls 20% over the same 60 bars. What does the ratio Close / BenchClose do?
Show the answer and why

Answer: It rises

The numerator shrank less than the denominator, so the ratio rises. Outperforming and rising are different statements - which is why a relative strength screen can return a list of falling stocks.

Question 2. What is wrong with this line, given that BenchClose is meant to hold the benchmark closes?
SetForeign( "^GSPC", "C" );
Show the answer and why

Answer: SetForeign takes fixup as its second argument, not a data field

Foreign() is (ticker, field, fixup); SetForeign() is (ticker, fixup, tradeprices). Passing "C" here sets fixup from a string instead of selecting a field, and no error is raised.

Question 3. Which of these are true of RelStrength("")? Select all that apply.
Show the answer and why

Answer: It uses the base security configured in Symbol - Categories for that symbol market, Its meaning can change if the symbol is moved to a different market

The empty-string form takes the base security from the Categories window, so database configuration is part of the answer. The official page documents the arguments but not the formula, and RelStrength is a cross-symbol comparison, not a single-symbol one.

Question 4. Two symbols have exactly the same 126-bar excess return over the benchmark. What can you conclude?
Show the answer and why

Answer: They finished the window at the same distance from the benchmark, and nothing more

A window return is a summary of the two endpoints. It discards the path, says nothing about drawdown along the way, and contains no forward-looking component at all.

Sources for this lesson

6 verified · checked 2026-08-31

  1. 01AFL Function Reference - RelStrengthamibroker.com/guide/afl/relstrength.html2026-08-31
  2. 02AFL Function Reference - RSIamibroker.com/guide/afl/rsi.html2026-08-31
  3. 03AFL Function Reference - SetForeignamibroker.com/guide/afl/setforeign.html2026-08-31
  4. 04AFL Function Reference - ROCamibroker.com/guide/afl/roc.html2026-08-31
  5. 05AmiBroker User's Guide - Categories window§ Base indexesamibroker.com/guide/w_categories.html2026-08-31
  6. 06AmiBroker User's Guide - Ranking functionalityamibroker.com/guide/h_ranking.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.