Ultimate Oscillator (UO)
I. What is the Ultimate Oscillator
The Ultimate Oscillator (UO) is an oscillator that fuses momentum information from three different time periods. It generates a composite momentum value by computing the weighted average of the ratios of buying pressure to true range across short, medium, and long periods. Its core design philosophy is: reducing the false signals commonly seen in single-period oscillators through multi-period fusion.
Historical Background
The Ultimate Oscillator was invented by Larry Williams in 1976 and formally published in 1985 in Technical Analysis of Stocks and Commodities magazine. Williams observed that most oscillators use only a single time period, leading to inconsistent performance across different market environments. He designed UO with the goal of creating a more robust, lower-false-signal momentum indicator by simultaneously considering short-term (7), medium-term (14), and long-term (28) periods.
Indicator Classification
- Type: Oscillator, displayed in a separate panel
- Category: Momentum
- Default Parameters: (short), (medium), (long)
- Value Range: 0 — 100
UO assigns weights of 4:2
(short:medium) to the three periods, giving the highest weight to short-term momentum. This reflects Williams’ philosophy: short-term changes are most sensitive, but require medium and long-term confirmation to filter out noise.II. Mathematical Principles and Calculation
Core Formulas
Step 1: Calculate Buying Pressure (BP)
Where:
- = current closing price
- = current low price
- = previous period’s closing price
Step 2: Calculate True Range (TR)
Step 3: Calculate averages for three periods
Step 4: Weighted composite UO
Step-by-Step Calculation Logic
- Calculate Buying Pressure BP: Close minus the lesser of current low and previous close
- Calculate True Range TR: Greater of current high and previous close minus the lesser of current low and previous close
- Sum BP and TR separately over 7/14/28 periods
- Calculate ratio for each period: Sum(BP) / Sum(TR)
- Weighted average: 4 x Avg7 + 2 x Avg14 + 1 x Avg28, divided by 7 and multiplied by 100
Meaning of Buying Pressure
BP measures the distance of the closing price from the true low. When the closing price is near the top of the range, BP approaches TR (strong buying power); when the closing price is near the bottom, BP approaches 0 (strong selling power).
UO uses separate sums of BP and TR before dividing, rather than applying SMA to the BP/TR ratio. This approach gives higher-volatility days (larger TR) greater weight, making it statistically more robust.
III. Python Implementation
import numpy as np
import pandas as pd
def ultimate_oscillator(high: pd.Series,
low: pd.Series,
close: pd.Series,
p1: int = 7,
p2: int = 14,
p3: int = 28) -> pd.Series:
"""
Calculate the Ultimate Oscillator (UO)
Parameters
----------
high : pd.Series
High price series
low : pd.Series
Low price series
close : pd.Series
Close price series
p1 : int
Short period, default 7
p2 : int
Medium period, default 14
p3 : int
Long period, default 28
Returns
-------
pd.Series
UO values (0-100)
"""
prev_close = close.shift(1)
# Buying Pressure
bp = close - pd.concat([low, prev_close], axis=1).min(axis=1)
# True Range
tr = pd.concat([high, prev_close], axis=1).max(axis=1) - \
pd.concat([low, prev_close], axis=1).min(axis=1)
# Averages for three periods
avg1 = bp.rolling(window=p1, min_periods=p1).sum() / \
tr.rolling(window=p1, min_periods=p1).sum()
avg2 = bp.rolling(window=p2, min_periods=p2).sum() / \
tr.rolling(window=p2, min_periods=p2).sum()
avg3 = bp.rolling(window=p3, min_periods=p3).sum() / \
tr.rolling(window=p3, min_periods=p3).sum()
# Weighted composite
uo = 100 * (4 * avg1 + 2 * avg2 + 1 * avg3) / 7
return uo
# ========== Usage Example ==========
if __name__ == "__main__":
np.random.seed(42)
dates = pd.date_range("2024-01-01", periods=120, freq="D")
base = 100 + np.cumsum(np.random.randn(120) * 1.0)
df = pd.DataFrame({
"date": dates,
"open": base + np.random.randn(120) * 0.3,
"high": base + np.abs(np.random.randn(120) * 0.8),
"low": base - np.abs(np.random.randn(120) * 0.8),
"close": base,
"volume": np.random.randint(1000, 10000, size=120),
})
df.set_index("date", inplace=True)
# Calculate UO
df["UO"] = ultimate_oscillator(df["high"], df["low"], df["close"])
print("=== Ultimate Oscillator Results (last 15 rows) ===")
print(df[["close", "UO"]].tail(15).to_string())
# Overbought/Oversold signals
df["signal"] = np.where(
df["UO"] > 70, -1, # Overbought
np.where(df["UO"] < 30, 1, 0) # Oversold
)
print("\n=== Overbought Signals (UO > 70) ===")
overbought = df[df["signal"] == -1]
print(overbought[["close", "UO"]].head(10).to_string())
print("\n=== Oversold Signals (UO < 30) ===")
oversold = df[df["signal"] == 1]
print(oversold[["close", "UO"]].head(10).to_string())
IV. Problems the Indicator Solves
1. Reducing False Signals
The greatest design advantage of UO is reducing false signals from single-period oscillators through multi-period fusion. In ranging markets, signals frequently triggered by short-period indicators are “diluted” by medium and long-term information.
2. Overbought / Oversold Detection
| Zone | Condition | Meaning |
|---|---|---|
| Overbought | UO > 70 | Multi-period momentum all strong, watch for pullback |
| Oversold | UO < 30 | Multi-period momentum all weak, watch for bounce |
3. Williams’ Buy Rules
Larry Williams defined strict buying conditions:
- UO first drops into oversold zone (< 30)
- A “bullish divergence” forms: price makes a new low but UO does not
- After the divergence, UO breaks above the prior rally high — Buy
- When UO > 70 or UO > 50 and then declines — Take profit
4. Williams’ Sell Rules
- UO first rises into overbought zone (> 70)
- A “bearish divergence” forms: price makes a new high but UO does not
- After the divergence, UO breaks below the prior pullback low — Sell
- When UO < 30 — Stop loss
5. Multi-Timeframe Consistency Confirmation
When UO is in an extreme zone, it means short, medium, and long-term momentum are all aligned, giving signals more credibility than single-period indicators.
Williams recommends that UO signals must be used in conjunction with divergence. Pure overbought/oversold readings alone do not constitute a complete trading decision. Only enter after divergence + breakout confirmation.
V. Advantages, Disadvantages, and Use Cases
Advantages
| Advantage | Description |
|---|---|
| Multi-period fusion | Considers short, medium, and long-term dimensions simultaneously for more robust signals |
| Fewer false signals | Significantly fewer false signals compared to single-period oscillators |
| Bounded range | 0-100 range makes defining overbought/oversold thresholds convenient |
| Solid theory | Based on buying pressure and true range with clear economic meaning |
Disadvantages
| Disadvantage | Description |
|---|---|
| Few signals | Strict trading rules result in very low signal frequency |
| Complex calculation | More calculation steps compared to RSI or Stochastic |
| Slow response | The long period (28) still slows response speed despite its lower weight |
| Subjective divergence identification | Williams’ rules require manual divergence assessment |
Use Cases
- Medium to long-term trading: The 28-period long component makes UO better suited for medium to long-term holding
- High-volatility markets: Multi-period fusion effectively filters noise in volatile markets
- Confirmation tool: When other indicators give signals, UO serves as multi-period confirmation
Comparison with Related Indicators
| Comparison | UO | RSI | Stochastic |
|---|---|---|---|
| Time Periods | Triple-period fusion | Single period | Single period |
| Value Range | 0-100 | 0-100 | 0-100 |
| False Signal Rate | Low | Medium | High |
| Signal Frequency | Low | Medium | High |
| Data Input | H/L/C | C | H/L/C |
- Strictly follow Williams’ rules: Overbought/oversold + divergence + breakout confirmation — all three steps are essential.
- Adjust thresholds: In strong markets, raise the overbought line to 75 and lower the oversold line to 25.
- Long-period filter: Use UO on the daily chart for overall direction, then use RSI or Stochastic on hourly charts for entries.
- Mind the weights: If you prefer longer-term signals, adjust the weights to 2:3 (reversing the short/long emphasis).