Williams %R

Haiyue
8min

I. What is Williams %R

Williams %R (also known as %R or W%R) is a momentum oscillator that measures the position of the current closing price relative to the price range over a given period. It is very similar to Stochastic %K, but %R expresses the “distance from the top of the range,” so its values are negative (-100 to 0), using inverse logic.

Historical Background

Williams %R was invented by the famous American trader Larry Williams. Larry Williams won the 1987 World Cup Trading Championship with a record-breaking return of 11,376%. The %R indicator became popular among short-term traders due to its simple calculation and direct signals.

Indicator Classification

  • Type: Oscillator, displayed in a separate panel
  • Category: Momentum
  • Default Parameters: Period n=14n = 14
  • Value Range: -100 to 0
%R and Stochastic %K Relationship

Williams %R and Fast Stochastic %K are mathematical mirror images: %R=%K100\%R = \%K - 100 When %K = 80 (overbought zone), %R = -20; when %K = 20 (oversold zone), %R = -80.


II. Mathematical Principles and Calculation

Core Formula

%R=HHnCtHHnLLn×(100)\%R = \frac{HH_n - C_t}{HH_n - LL_n} \times (-100)

Where:

  • CtC_t = current closing price
  • HHnHH_n = highest high over the past nn periods
  • LLnLL_n = lowest low over the past nn periods
  • nn = lookback period (default 14)

Equivalent Expression

%R=100+CtLLnHHnLLn×100\%R = -100 + \frac{C_t - LL_n}{HH_n - LL_n} \times 100

It can be seen that %R=%Kfast100\%R = \%K_{fast} - 100.

Step-by-Step Calculation Logic

  1. Determine lookback period nn (default 14)
  2. Calculate range extremes: Find the highest high HHnHH_n and lowest low LLnLL_n over the past nn periods
  3. Calculate distance from top: HHnCtHH_n - C_t
  4. Divide by range width: (HHnCt)/(HHnLLn)(HH_n - C_t) / (HH_n - LL_n)
  5. Multiply by -100: Map the result to the -100 to 0 range

Value Interpretation

%R ValueMeaning
0Closing price equals the range high
-50Closing price is at the midpoint of the range
-100Closing price equals the range low
Why Negative Values?

Larry Williams chose the negative range (-100 to 0) to visually represent the “distance from the top” on charts. The closer %R is to 0, the closer the price is to the range top (overbought); the closer to -100, the closer the price is to the range bottom (oversold). This design, while inverted compared to Stochastic, offers its own visual advantages.


III. Python Implementation

import numpy as np
import pandas as pd


def williams_r(high: pd.Series,
               low: pd.Series,
               close: pd.Series,
               period: int = 14) -> pd.Series:
    """
    Calculate Williams %R

    Parameters
    ----------
    high : pd.Series
        High price series
    low : pd.Series
        Low price series
    close : pd.Series
        Close price series
    period : int
        Lookback period, default 14

    Returns
    -------
    pd.Series
        Williams %R values (-100 to 0)
    """
    highest_high = high.rolling(window=period, min_periods=period).max()
    lowest_low = low.rolling(window=period, min_periods=period).min()

    wr = (highest_high - close) / (highest_high - lowest_low) * (-100)

    return wr


# ========== 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 Williams %R
    df["WR_14"] = williams_r(df["high"], df["low"], df["close"], period=14)

    print("=== Williams %R Results (last 15 rows) ===")
    print(df[["close", "WR_14"]].tail(15).to_string())

    # Overbought/Oversold signals
    df["signal"] = np.where(
        df["WR_14"] > -20, -1,    # Overbought (near 0)
        np.where(df["WR_14"] < -80, 1, 0)  # Oversold (near -100)
    )

    print("\n=== Overbought Signals (%R > -20) ===")
    overbought = df[df["signal"] == -1]
    print(overbought[["close", "WR_14"]].head(10).to_string())

    print("\n=== Oversold Signals (%R < -80) ===")
    oversold = df[df["signal"] == 1]
    print(oversold[["close", "WR_14"]].head(10).to_string())

IV. Problems the Indicator Solves

1. Overbought / Oversold Detection

ZoneConditionMeaning
Overbought%R > -20Price near range top, pullback likely
Oversold%R < -80Price near range bottom, bounce likely
Neutral-80 <= %R <= -20No extreme signal

2. Trend Confirmation

  • %R operating between -50 and 0 — bullish trend
  • %R operating between -100 and -50 — bearish trend
  • %R crossing back and forth around -50 — oscillating, no trend

3. Divergence Signals

  • Bearish Divergence: Price makes a new high, %R fails to reach a higher level — upward momentum exhausted
  • Bullish Divergence: Price makes a new low, %R fails to reach a lower level — downward momentum exhausted

4. Failure Swing

Similar to RSI:

  • %R pulls back from overbought zone, rallies but fails to re-enter overbought — short signal
  • %R bounces from oversold zone, declines but fails to re-enter oversold — long signal
Caution

%R has no built-in smoothing mechanism, so signal fluctuations are significant. In practice, many traders apply an additional SMA(3) or EMA(3) smoothing to %R to reduce noise.


V. Advantages, Disadvantages, and Use Cases

Advantages

AdvantageDescription
Extremely simpleSimple formula, fast computation
Highly responsiveNo built-in smoothing, directly reflects price position within range
Intuitive thresholds-20 and -80 boundaries are clear
Complements StochasticProvides a reverse perspective, convenient for cross-validation

Disadvantages

DisadvantageDescription
High noiseNo smoothing leads to frequent false signals
Saturates in trendsStays in overbought/oversold zones during strong trends
Negative value intuition-100 to 0 inverse logic is less intuitive than 0-100
Needs confirmationNot recommended as a standalone trading decision basis

Use Cases

  • Short-term trading: Suited for quick in-and-out trading styles
  • Range-bound markets: Overbought/oversold signals work well within clear support/resistance zones
  • Alternative to Stochastic: When a more raw, sensitive signal is needed
ComparisonWilliams %RStochasticRSI
Value Range-100 to 00-1000-100
SmoothingNoneDual SMA smoothingWilder Smoothing
SensitivityVery highHighMedium
NoiseHighMediumLow
Math Relationship%K - 100IndependentIndependent
Practical Tips
  1. Add smoothing: In practice, apply a 3-5 period SMA smoothing to %R before use.
  2. Combine with trend indicators: Use moving averages or ADX to determine trend direction, then use %R to find entries.
  3. Watch for exits from extreme zones: The moment %R crosses above -80 from oversold or below -20 from overbought is more meaningful than staying in extreme territory.
  4. Cross-validate with Stochastic: Signals from both should agree; if they diverge, stay on the sidelines.