The Bollinger Bands post and the SMA cross post
drew their lines on the price chart, in dollars. The three indicators here live in a panel of
their own, on a scale that never changes: 0 to 100 for the first two, a percentage around zero
for the third. Murphy
names the two problems that scale solves. A plain momentum line (today’s price minus the price
ten days ago) jumps when a big day falls out of the back of the window even if today is quiet,
and it has no fixed range, so “high” on one stock is nothing on another. An oscillator is an
indicator built to fix both: smoothed, and bounded. Kirkpatrick and Dahlquist
define the family more tightly: an oscillator is calculated from two moving averages, usually as
a ratio of one to the other, which dampens the day-to-day noise and makes it “somewhat
bounded”, with extremes “beyond which they rarely go”. Their warning comes in the same breath.
The overbought and oversold levels “have been arbitrarily determined for each oscillator”, and
mechanical signals from an oscillator alone “have not proven to be successful”, because how it
behaves depends on whether the price is trending. Keep that in mind through what follows: each
indicator is described with its traditional lines, and the evidence on those lines comes at the
end of each section. The post builds three of them on SPY’s closes
from 2019 to 2023, works each by hand on 2020-03-16, the Monday the index fell eleven percent,
and then asks how much they agree.
RSI: the average up-day against the average down-day
The relative strength index was introduced by Welles Wilder in 1978, and both
Achelis
and Murphy pause on the name: it does not compare one stock with another, it compares a stock’s
own up-days with its own down-days. Over the last 14 days, average the gains on the days the
close rose and, separately, the losses on the days it fell. Their ratio is the relative strength,
RS, and the RSI squeezes it into 0 to 100. The averages are not plain means after the first day:
Wilder’s rule, as the
StockCharts
page writes it, carries yesterday’s average forward with weight 13 and lets today in with weight
1, which is a slow exponential smoothing in disguise.
RSI, the result on the 0 to 100 scaleG, the average gain over up-daysD, the average loss over down-days
On 2020-03-16 the close fell $26.80. The day before, the average gain stood at 3.333
and the average loss at 5.689. Today’s gain is zero, so the average gain shrinks to
3.094; today’s loss is 26.80, so the average loss rises to 7.196. Their ratio
is 0.430, and 100 minus 100 over 1.430 gives an RSI of 30.1.
An eleven-percent day moved the RSI only to 30, because each day enters with a weight of one
in fourteen. The RSI had already bottomed at 19.8 on 2020-02-28, in the first week of the
fall, and stood at 29.6 on the price low of 2020-03-23. Both books call this a divergence, the
indicator turning before the price; here it turned three weeks early, with the index still to fall another 24%.
Wilder’s lines are 70 and 30: above 70 the stock is “overbought”, below 30 “oversold”. Murphy
adds that the lines drift with the market, 80 in a bull market and 20 in a bear one, and SPY
shows why. From 2019 to 2023 the index rose, and its RSI spent 11.0% of days above 70 and only
1.5% below 30; its whole range was 19.8 to 82.3. The 30 line was reached in one crash, the 70
line in every good quarter.
Kirkpatrick and Dahlquist report what happens when the lines are tested as signals, and the
answer is unkind to the usual reading. Two studies they summarise, Bauer and Dahlquist (1999,
878 stocks over 1985 to 1996) and Thomas (2003, 1,750 stocks over 1995 to 2001), found that
selling when the RSI drops back under 70 and buying when it climbs back over 30 had “marginal
returns”, a result the authors call “disappointing”, while the opposite rule, buying when the
RSI is high, “outperformed all signals”. A trading magazine’s test on nineteen futures over
1994 to 2003, buying when the 14-day RSI crossed above 75 and selling at 25 or after twenty
days, gave “a steady, straight equity curve”. Kirkpatrick and Dahlquist name this the “RSI is
wrong” method, and SPY’s 11.0% of days above 70 in a market that doubled is the same picture:
a high RSI in a rising market mostly means the market is rising. Both studies tested without
regard to trend and over periods that rose, and the authors say so; the lesson is not that 70
is a buy, it is that 70 is not a sell.
The RSI depends on where you start it
The smoothing never fully forgets, so the RSI on a given day depends slightly on how much history
was fed in before it. StockCharts says it uses at least 250 days before the start of a chart for
that reason. The numbers here start the chain on the first day of 2019 with plain means over the
first fourteen changes (an average gain of 1.554 and an average loss of 0.711), so a chart that
starts earlier will differ in the second decimal place.
Stochastic %K: where today’s close sits in the recent range
Nobody is sure who invented the stochastic oscillator. Kirkpatrick and Dahlquist trace it to
a 1950s set of lessons on Elliott waves, credit George Lane with promoting it from 1954, and report that
the name came from the words “stochastic process” handwritten on the lesson material, which a
software vendor adopted in 1978 because the indicator needed a name; it has nothing to do with
randomness. The idea starts from an observation Murphy states in one line: in
an uptrend, closes tend to sit near the top of each day’s range, and in a downtrend near the
bottom. The indicator makes that a number for a window rather than a day. Take the highest high
and the lowest low of the last 14 days; %K is how far up that range today’s close sits, as a
percentage. A 3-day average of %K is called %D, and the pair is drawn together.
%K, the close's place in the 14-day rangeC, today's close
The blog’s data has closing prices only, so the “highest high” and “lowest low” here are the
highest and lowest close of the window, which makes the range a little narrower than a
chart’s. On 2020-03-16 the highest close of the last 14 days was 284.47, on 2020-03-04, and the
lowest was 218.09: that day’s own close. The close is at the bottom of its range, so
%K is exactly 0.0, and %D, the mean of the last three, is 10.9.
That is the stochastic’s character. It cannot see how far the price fell, only where today’s
close lies between the window’s two ends, so any day that sets a new 14-day low reads zero and
any day that sets a new high reads 100. On SPY it sat above 80 on 49.6% of days and below 20 on
18.8%, because a rising index keeps closing near the top of its recent range. The books say this
in their own ways: Achelis gives 20 and 80 as the usual lines, and StockCharts warns that an
overbought reading in a strong uptrend does not mean a decline. Murphy’s version of the signal
waits for %D to be past a line and diverging from the price, and only then takes the %K line
crossing %D as the trigger.
Kirkpatrick and Dahlquist place the stochastic against the other two by what it does with the
latest close. The RSI and the PPO smooth it in with weeks of history; the stochastic reports it
raw, as a fraction of the window’s range, which is why it is “very sensitive to recent action”
and why the slow version, “a moving average of the moving average of %K”, exists. Their
reading of the tests matches the RSI’s: Thomas found the extreme readings “somewhat predictive”
and the standard overbought and oversold rules a failure, and Bauer and Dahlquist found buying
at the lows worked better than selling at the highs over a period that rose. In a trending
market, they add, the oscillator “often will not reach one or the other extreme”, so a sell
signal in a rising trend “must be suspect”: SPY’s %K spent half of five years above 80.
Fast, slow and full
Achelis lists four settings: the %K window, a “slowing” period that averages %K before it is
drawn, the %D window, and the average type. A slowing of 1 is the fast stochastic, shown here; a
slowing of 3 is the slow stochastic, which Murphy says most traders use because its signals are
steadier. StockCharts’ “full” version exposes all three numbers, (14, 3, 3) by default.
PPO: the gap between two averages, in percent
The third indicator is the SMA cross post’s two averages turned into a line. Achelis’s price
oscillator is the difference between a short and a long moving average, “almost identical to
the MACD” except that the MACD fixes the windows at 12 and 26 days and reports the gap in
dollars. The percentage price oscillator divides the gap by the long average, so a reading of
−6% means the same on any stock. It is drawn with a signal line, a 9-day average of itself, and
a histogram, the difference between the two. The averages are exponential: each day’s value is
yesterday’s plus a fraction of today’s surprise, the fraction being two over the window plus one.
On 2020-03-16 the 12-day exponential average was 254.33 and the 26-day was 270.71. The gap is
−16.38, which is the MACD in dollars, and divided by 270.71 it is a PPO of −6.05%. The
signal line stood at −3.90% and the histogram at −2.16%, the line pulling away from its own
average. Unlike the other two, the PPO has no fixed ceiling: on SPY it ran from −8.48% to
+3.06%, and it was above zero on 71.5% of days, which is one more way of saying the index rose.
Achelis’s assessment of the price oscillator is the SMA cross post’s lesson in a sentence: it
“does an outstanding job of keeping you on the right side of the market during trending periods”
and “produces small losses” in the choppy ones.
Kirkpatrick and Dahlquist describe the MACD, and so the PPO, as “a variation of the moving
average crossover”, and they are precise about what the line adds. A crossing of zero is
“just the same signals as would be generated from a moving average crossover”, the
SMA cross post in a different panel. The signal line adds timing inside a
trend: with the PPO above zero, an upward crossing of the signal line is a buy, and “downward
crossings are not at all reliable while the trend is upward”. The histogram, the gap between
the two lines, narrows before they cross, which is what the fourth panel below shows through
April 2020.
Which average goes in the denominator
Achelis’s percentage version divides the gap by the shorter average; StockCharts’ PPO divides
by the longer one, and that is the convention charting software follows and the one used here.
On 2020-03-16 the two give −6.05% and −6.44%. They disagree most when the averages are far
apart, which is exactly when the reading is extreme.
Three readings of one crash
Here are the three on the eleven-percent day and on a quiet day in a rising market,
2021-08-13:
Day
RSI (0 to 100)
Stochastic %K (0 to 100)
PPO (% of the 26-day average)
2020-03-16, an eleven-percent fall
30.1
0.0
−6.05
2021-08-13, a calm uptrend
66.9
100.0
+0.85
All three agree on the sign of each day, and they differ in speed. The stochastic is the
fastest: %K read zero on the fall and 100 on the calm day, and after the 2020 low it was back
above 20 the very next day, 2020-03-24. The RSI is slower, needing until 2020-04-08 to climb
back over 50. The PPO is the slowest, because a 26-day average takes weeks to turn: it bottomed
on the price low itself, 2020-03-23, and did not cross back above zero until 2020-04-16. Over
the five years the RSI and the stochastic moved together (correlation 0.85), the RSI and the
PPO less so (0.72), and the stochastic and the PPO least (0.48). The
indicators post argued that three indicators from one window are
one reading three times; these three use different windows and different arithmetic, and the
0.48 is what partly independent evidence looks like. Kirkpatrick and Dahlquist put the same
point bluntly: price oscillators “do not add new information”, being “manipulations of the
same data”, and “watching several different price oscillators calculated over the same period
is silly”. Different windows, or a different input such as volume, are where new evidence
comes from.
Traps
People think
Actually
RSI below 30 marks the bottom
it marked 2020-02-28, three weeks before the low; the lines are where the index is stretched, not where it turns
%K at 100 means overbought, sell
it means today’s close is the highest of 14 days, which a rising market does half the time
PPO and MACD are different signals
the same gap, in percent against dollars; PPO reads across stocks and years, MACD does not
An oscillator predicts
each one describes the last few weeks on a fixed scale; the forecast is the reader’s
RSI above 70 is a sell
the tests Kirkpatrick and Dahlquist report found the opposite rule, buying strength above 70 or 75, did better in rising markets
Three indicators agreeing is strong evidence
RSI and %K agree 0.85 of the time by construction; %K and PPO, 0.48, are closer to two opinions
The code
The snippet reproduces the three readings on 2020-03-16. Variables:
Variable
Holds
px
SPY’s adjusted close, one row per trading day
gain, loss
each day’s rise or fall in the close, the other one being zero
g, l
Wilder’s running average gain and average loss, one value per day
k
stochastic %K from closes, one value per day
ppo
the percentage price oscillator, one value per day
import pandas as pdurl = "https://raw.githubusercontent.com/ankurtrapasiya/examples/main/data/prices.csv"px = pd.read_csv(url, index_col="date", parse_dates=True)["SPY"]day = "2020-03-16"change = px.diff()gain, loss = change.clip(lower=0), (-change).clip(lower=0)g, l = gain.iloc[1:15].mean(), loss.iloc[1:15].mean() # Wilder's start: plain means of the first 14for i in range(15, px.index.get_loc(day) + 1): # then carry forward with weight 13, add today with weight 1 g = (13 * g + gain.iloc[i]) / 14 l = (13 * l + loss.iloc[i]) / 14print(f"RSI {100 - 100 / (1 + g / l):.1f}")w = px.loc[:day].iloc[-14:] # the last 14 closes, as the rangeprint(f"%K {100 * (w.iloc[-1] - w.min()) / (w.max() - w.min()):.1f}")e12 = px.ewm(span=12, adjust=False).mean() # span=n is the 2/(n+1) exponential averagee26 = px.ewm(span=26, adjust=False).mean()print(f"PPO {100 * (e12[day] - e26[day]) / e26[day]:+.2f}%")
It prints an RSI of 30.1, a %K of 0.0 and a PPO of −6.05%. .ewm(span=n, adjust=False) is the
pandas idiom for the recursive exponential average with the two-over-n-plus-one weight; it starts
the chain from the first close rather than from a 26-day mean, which after a year of data makes
no visible difference.
The script behind the real numbers
The tables and the chart come from
examples/rsi-stochastic-ppo/oscillators.py,
which runs on the blog’s five tickers. To run it yourself, clone the examples repo: its README has a three-command Docker setup, or run uv run rsi-stochastic-ppo/oscillators.py.
Summary
Idea
In one line
On SPY, 2020-03-16
An oscillator is bounded and smoothed
a fixed scale, so a reading means the same at any price
all three on their own scale while the price fell 11%
RSI
average up-day over average down-day, 14 days, Wilder’s slow smoothing
30.1; bottomed at 19.8 three weeks before the price low
Stochastic %K
where today’s close sits in the 14-day range
0.0, a new low; above 80 on half of all days
PPO
the 12-day average’s lead over the 26-day, in percent
−6.05%; last to recover, above zero again on 2020-04-16
Different arithmetic, partly different evidence
the pair that share least agree least
%K and PPO correlate 0.48, RSI and %K 0.85
References
John J. Murphy, Technical Analysis of the Financial Markets (New York Institute of Finance, 1999). Chapter 10, “Oscillators and Contrary Opinion”, for the two problems an oscillator solves, the RSI formula and its 70 and 30 lines, failure swings, and the stochastic’s %K and %D with Lane’s observation about closes.
Steven B. Achelis, Technical Analysis from A to Z, 2nd edition (McGraw-Hill, 2001). The entries “Relative Strength Index”, “Stochastic Oscillator” and “Price Oscillator” for the origins, the four stochastic settings, and the price oscillator as a percentage of the shorter average.
Charles D. Kirkpatrick and Julie R. Dahlquist, Technical Analysis: The Complete Resource for Financial Market Technicians (FT Press, 2007). Chapter 18, “Confirmation”, for the definition of an oscillator and the warning about arbitrary levels, Wilder’s smoothing, the “RSI is wrong” method and the Bauer and Dahlquist and Thomas tests, the origin of the stochastic and its name, the MACD as a crossover variation, and why several price oscillators over one period are redundant.