The Awesome Oscillator is a two-line indicator in disguise — a difference of two simple moving averages of the bar midpoint, as the explainer covered. Here it is in pandas, including the momentum bar colours and a saucer signal.
What you’ll need
- Python 3.9+,
pandas high,lowseries.
From scratch
import pandas as pd
def awesome_oscillator(high, low, fast: int = 5, slow: int = 34):
median = (high + low) / 2
return (median.rolling(fast).mean()
- median.rolling(slow).mean()).rename("ao")
That’s the whole indicator: median price (H+L)/2, then the 5-period SMA minus the
34-period SMA. No EMA, no signal line, no close — the three things that make it an
AO rather than a MACD.
Bar colours and signals
The classic display colours each bar by whether AO rose or fell:
ao = awesome_oscillator(df["high"], df["low"])
rising = ao > ao.shift(1) # green when momentum increased, else red
# Zero-line cross.
df["ao_bull"] = (ao > 0) & (ao.shift(1) <= 0)
# Bull saucer: AO above zero, two falling bars then a rising bar.
above = ao > 0
two_down = (ao.shift(1) < ao.shift(2)) & (ao.shift(2) < ao.shift(3))
df["ao_saucer"] = above & two_down & (ao > ao.shift(1))
ao_bull and ao_saucer are concrete signals to test in
AlgoGen — the saucer especially benefits from a backtest,
since it fires often.
Gotchas
- Median price, not close.
(high + low) / 2. Using close turns it into something closer to a plain MACD variant. - SMA, not EMA. The AO uses simple moving averages; swapping in EMAs changes the line.
- Fixed 5/34. These are the indicator’s identity; changing them makes it a generic SMA-difference oscillator, not “the AO.”
Bonus: the Accelerator Oscillator
Williams’ companion indicator is one more line — the AO minus a 5-SMA of itself:
def accelerator(high, low):
ao = awesome_oscillator(high, low)
return (ao - ao.rolling(5).mean()).rename("ac")
The AC measures the acceleration of momentum (the change in the AO), coloured the same green/red way. Rising AC above zero is Williams’ strongest “go” condition.
The lazy one-liner
import pandas_ta as ta
df["ao"] = ta.ao(df["high"], df["low"]) # 5/34 by default
Same indicator elsewhere: MQL5, Pine Script, EasyLanguage, NinjaScript.
This post is educational, not financial advice. Indicators describe the past; they don’t predict the future. Backtest anything before you risk real money on it.
Sources and further reading
- Windowing operations (pandas documentation)
