Ultimate Oscillator (UO)

Haiyue
10min

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: p1=7p_1 = 7 (short), p2=14p_2 = 14 (medium), p3=28p_3 = 28 (long)
  • Value Range: 0 — 100
Weighting Approach

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)

BPt=Ctmin(Lt,Ct1)BP_t = C_t - \min(L_t, C_{t-1})

Where:

  • CtC_t = current closing price
  • LtL_t = current low price
  • Ct1C_{t-1} = previous period’s closing price

Step 2: Calculate True Range (TR)

TRt=max(Ht,Ct1)min(Lt,Ct1)TR_t = \max(H_t, C_{t-1}) - \min(L_t, C_{t-1})

Step 3: Calculate averages for three periods

Avg7=i=06BPtii=06TRtiAvg_7 = \frac{\sum_{i=0}^{6} BP_{t-i}}{\sum_{i=0}^{6} TR_{t-i}}

Avg14=i=013BPtii=013TRtiAvg_{14} = \frac{\sum_{i=0}^{13} BP_{t-i}}{\sum_{i=0}^{13} TR_{t-i}}

Avg28=i=027BPtii=027TRtiAvg_{28} = \frac{\sum_{i=0}^{27} BP_{t-i}}{\sum_{i=0}^{27} TR_{t-i}}

Step 4: Weighted composite UO

UO=100×4×Avg7+2×Avg14+1×Avg284+2+1UO = 100 \times \frac{4 \times Avg_7 + 2 \times Avg_{14} + 1 \times Avg_{28}}{4 + 2 + 1}

Step-by-Step Calculation Logic

  1. Calculate Buying Pressure BP: Close minus the lesser of current low and previous close
  2. Calculate True Range TR: Greater of current high and previous close minus the lesser of current low and previous close
  3. Sum BP and TR separately over 7/14/28 periods
  4. Calculate ratio for each period: Sum(BP) / Sum(TR)
  5. 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).

Why Sum Rather Than SMA?

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

ZoneConditionMeaning
OverboughtUO > 70Multi-period momentum all strong, watch for pullback
OversoldUO < 30Multi-period momentum all weak, watch for bounce

3. Williams’ Buy Rules

Larry Williams defined strict buying conditions:

  1. UO first drops into oversold zone (< 30)
  2. A “bullish divergence” forms: price makes a new low but UO does not
  3. After the divergence, UO breaks above the prior rally high — Buy
  4. When UO > 70 or UO > 50 and then declines — Take profit

4. Williams’ Sell Rules

  1. UO first rises into overbought zone (> 70)
  2. A “bearish divergence” forms: price makes a new high but UO does not
  3. After the divergence, UO breaks below the prior pullback low — Sell
  4. 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.

Caution

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

AdvantageDescription
Multi-period fusionConsiders short, medium, and long-term dimensions simultaneously for more robust signals
Fewer false signalsSignificantly fewer false signals compared to single-period oscillators
Bounded range0-100 range makes defining overbought/oversold thresholds convenient
Solid theoryBased on buying pressure and true range with clear economic meaning

Disadvantages

DisadvantageDescription
Few signalsStrict trading rules result in very low signal frequency
Complex calculationMore calculation steps compared to RSI or Stochastic
Slow responseThe long period (28) still slows response speed despite its lower weight
Subjective divergence identificationWilliams’ 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
ComparisonUORSIStochastic
Time PeriodsTriple-period fusionSingle periodSingle period
Value Range0-1000-1000-100
False Signal RateLowMediumHigh
Signal FrequencyLowMediumHigh
Data InputH/L/CCH/L/C
Practical Tips
  1. Strictly follow Williams’ rules: Overbought/oversold + divergence + breakout confirmation — all three steps are essential.
  2. Adjust thresholds: In strong markets, raise the overbought line to 75 and lower the oversold line to 25.
  3. Long-period filter: Use UO on the daily chart for overall direction, then use RSI or Stochastic on hourly charts for entries.
  4. Mind the weights: If you prefer longer-term signals, adjust the weights to 2:3
    (reversing the short/long emphasis).