How to Create Williams %R on TradingView
Plot bounded momentum, classic overbought and oversold bands, and compare signals with RSI thinking.
Williams %R is a fast-reacting momentum oscillator that measures where the latest close sits within the recent high-low window. Larry Williams popularized the tool for spotting overbought and oversold stretches, often with -20 and -80 guides. This tutorial breaks down the math, contrasts Williams %R with RSI, and ships a Pine Script v5 indicator using ta.wpr() with alerts, fills, and a compact cheat sheet table.
What is Williams %R?
Williams %R compares the closing price to the highest high of the lookback window, scaled to a 0 to -100 range (TradingView’s implementation follows the classic negative scale).
Values near 0 indicate closes are hugging the top of the range (strong recent buying pressure within the window). Values near -100 show closes near the bottom of the range.
Unlike RSI, which uses average gains and losses, Williams %R is entirely anchored to the raw high-low envelope, which makes it especially sensitive to sharp spikes and short-term reversals.
Why Traders Use This Indicator
- Flag short-term overbought and oversold pockets relative to recent range extremes.
- Confirm breakouts when %R holds in the “upper chamber” during momentum phases.
- Diverge price versus %R for early warning of fading thrust—always confirm with structure.
- Pair with slower filters so choppy ranges do not generate excessive false flips.
| Parameter | Default | Description |
|---|---|---|
| Length | 14 | Matches many default RSI settings for easy side-by-side comparison, though you should tune per market. |
| Overbought / oversold | -20 / -80 | Common horizontal guides. In strong trends, %R can remain pinned in one chamber for extended runs. |
| Scale | 0 to -100 | TradingView plots the negative convention; some legacy platforms invert or rescale—mind translation. |
| Confirmation | Structure or trend filter | Use higher timeframe bias or price action triggers so oscillator extremes become context, not blind entries. |
How Williams %R is calculated
Let Hn be the highest high over the last n bars and Ln the lowest low. With close C:
%R = -100 × (Hn - C) / (Hn - Ln)
When C = Hn, the numerator is zero and %R is 0. When C = Ln, %R is -100. This example returns an unavailable value when the high-low range is zero; it does not invent a neutral reading.
Pine’s ta.wpr(length) computes the series. See TradingView’s Williams %R documentation for the formula and standard reference levels.
Signal Interpretation
Above -20: Often treated as overbought, but in momentum regimes price can stay “overbought” while grinding higher—think continuation, not automatic short.
Below -80: Mirrors oversold language; confirm with bullish structure for reversals.
Crosses of midline (-50): Some systems use the midpoint as a coarse bias toggle—less common than RSI 50-line plays but analogous in spirit.
Divergences: Bearish when price prints higher highs while %R makes lower highs; bullish for the inverse. Validate with volume or candlestick triggers.
Combining with Other Indicators
Blend Williams %R with a 200-period moving average or market structure rules so shorts at -20 only occur in downtrends and longs near -80 only in uptrends. Add ATR for stop spacing and MACD histogram for slower momentum agreement.
Writing Pine Script Manually
Challenges of Manual Coding
Hand-code %R using ta.highest and ta.lowest and diff against ta.wpr().
Plot RSI in the same pane with normalized scaling notes for educators.
Require two closes beyond -20 before firing a “confirmed overbought” alert.
Build a histogram of time spent below -80 to study mean-reversion edge cases.
//@version=5
indicator("Williams %R Tutorial (ta.wpr)", shorttitle="W%R", overlay=false)
// -----------------------------------------------------------------------------
// Inputs
// -----------------------------------------------------------------------------
length = input.int(14, "Length", minval=1)
ob = input.float(-20.0, "Overbought (upper band)", minval=-100, maxval=0, step=1)
os = input.float(-80.0, "Oversold (lower band)", minval=-100, maxval=0, step=1)
showMid = input.bool(true, "Show -50 mid reference")
showFill = input.bool(true, "Fill OB/OS chambers")
showRSI = input.bool(false, "Plot RSI(14) secondary (scaled note)")
// -----------------------------------------------------------------------------
// Core series
// -----------------------------------------------------------------------------
if ob <= os
runtime.error("The upper band must be greater than the lower band.")
rangeHigh = ta.highest(high, length)
rangeLow = ta.lowest(low, length)
wprValue = ta.wpr(length)
w = rangeHigh > rangeLow ? wprValue : na
// RSI for optional educational overlay (distinct scale—interpret carefully)
r = ta.rsi(close, 14)
// -----------------------------------------------------------------------------
// Chamber logic
// -----------------------------------------------------------------------------
inOB = w > ob
inOS = w < os
rawCrossUp = ta.crossover(w, os)
rawCrossDown = ta.crossunder(w, ob)
rawCrossMid = ta.cross(w, -50)
crossUpOS = barstate.isconfirmed and rawCrossUp
crossDnOB = barstate.isconfirmed and rawCrossDown
// -----------------------------------------------------------------------------
// Plots
// -----------------------------------------------------------------------------
plot(w, title="Williams %R", color=color.new(color.purple, 0), linewidth=2)
plot(showRSI ? r - 100.0 : na, title="RSI shifted for rough compare", color=color.new(color.teal, 65), linewidth=1)
hline(0, "0", color=color.new(color.gray, 70))
hline(-100, "-100", color=color.new(color.gray, 70))
hline(ob, "OB", color=color.new(color.red, 40))
hline(os, "OS", color=color.new(color.green, 40))
plot(showMid ? -50.0 : na, title="-50 mid reference", color=color.new(color.gray, 75), linewidth=1, style=plot.style_circles)
// -----------------------------------------------------------------------------
// Background fills
// -----------------------------------------------------------------------------
bgcolor(showFill and inOB ? color.new(color.red, 88) : na)
bgcolor(showFill and inOS ? color.new(color.green, 88) : na)
// -----------------------------------------------------------------------------
// Markers on chamber exits / entries (illustrative)
// -----------------------------------------------------------------------------
plotshape(crossUpOS, title="Lift off oversold", style=shape.triangleup, location=location.bottom, size=size.tiny, color=color.new(color.lime, 0))
plotshape(crossDnOB, title="Roll from overbought", style=shape.triangledown, location=location.top, size=size.tiny, color=color.new(color.red, 0))
// -----------------------------------------------------------------------------
// Alerts
// -----------------------------------------------------------------------------
alertcondition(crossUpOS, title="W%R left oversold zone", message="Williams %R crossed above oversold threshold")
alertcondition(crossDnOB, title="W%R left overbought zone", message="Williams %R crossed below overbought threshold")
alertcondition(barstate.isconfirmed and rawCrossMid, title="W%R crossed -50", message="Williams %R crossed midpoint")
// -----------------------------------------------------------------------------
// Stats table (last bar)
// -----------------------------------------------------------------------------
var table tb = table.new(position.top_right, 2, 7, border_width=1)
if barstate.islast
table.cell(tb, 0, 0, "W%R(" + str.tostring(length) + ")", text_color=color.white, bgcolor=color.new(color.gray, 35))
table.cell(tb, 1, 0, str.tostring(w, "#.##"), text_color=color.white)
table.cell(tb, 0, 1, "Status", text_color=color.white, bgcolor=color.new(color.gray, 35))
st = na(w) ? "Unavailable" : inOB ? "Overbought chamber" : inOS ? "Oversold chamber" : "Neutral"
table.cell(tb, 1, 1, st, text_color=color.white)
table.cell(tb, 0, 2, "Dist to OB", text_color=color.white, bgcolor=color.new(color.gray, 35))
table.cell(tb, 1, 2, str.tostring(ob - w, "#.##"), text_color=color.white)
table.cell(tb, 0, 3, "Dist to OS", text_color=color.white, bgcolor=color.new(color.gray, 35))
table.cell(tb, 1, 3, str.tostring(w - os, "#.##"), text_color=color.white)
table.cell(tb, 0, 4, str.tostring(length) + "-bar high", text_color=color.white, bgcolor=color.new(color.gray, 35))
table.cell(tb, 1, 4, str.tostring(rangeHigh, format.mintick), text_color=color.white)
table.cell(tb, 0, 5, str.tostring(length) + "-bar low", text_color=color.white, bgcolor=color.new(color.gray, 35))
table.cell(tb, 1, 5, str.tostring(rangeLow, format.mintick), text_color=color.white)
table.cell(tb, 0, 6, "Note", text_color=color.white, bgcolor=color.new(color.gray, 35))
table.cell(tb, 1, 6, "RSI line shifted", text_color=color.white)
// -----------------------------------------------------------------------------
// Implementation notes
// -----------------------------------------------------------------------------
// 1) ta.wpr uses the canonical TradingView definition on 0..-100 scale.
// 2) RSI overlay is shifted by -100 for rough visual comparison only—not equivalent math.
// 3) Markers and alerts require bar confirmation; no trading performance is implied.Maintenance Note: Remove or gate the shifted RSI plot before publishing to traders who might misread the overlay. Markers and alerts here require bar confirmation; the plotted open-bar value still changes. Create the named alert in TradingView and recreate it after changing inputs.
Build with Pineify Visual Editor
Pineify lets you configure supported indicators and export Pine Script. Available settings and plan requirements vary by indicator. Compare the generated rules with the example before using its signals.
| Feature | Manual Pine Script | Pineify Visual Editor |
|---|---|---|
| Williams %R calculation | Call ta.wpr(length); a hand-coded formula is optional. | The Williams %R visual block uses ta.wpr and exposes Length. |
| Display | Write plot and band styling in Pine. | Configure the WPR Line and Band Color in the block. |
| Custom tutorial features | The supplied example adds confirmed alerts, an optional shifted RSI and a table. | These additions are custom code; request them from the AI Coding Agent and inspect the result rather than assuming every control exists in the visual block. |
Configure the existing Williams %R block with a Length input and WPR output.
Adjust WPR Line and Band Color settings.
Inspect exported source and compare the same symbol, timeframe and length in TradingView.
Step-by-Step Tutorial
Add Williams %R
Create a separate-pane indicator in the Visual Editor and add the Williams %R block.
Set the length
The block defaults to 14. Choose a test length and keep it consistent when comparing charts.
Style the output
Configure WPR Line and Band Color. Custom tables or the shifted RSI in this tutorial require additional code.
Export and inspect
Add the export to TradingView and compare closed-bar values with the built-in Williams %R. If custom alerts are added, verify their bar-close behavior before creating an alert.
Trading Strategies & Pro Tips
Trend-filtered mean reversion
Test a predefined oversold-exit rule with a moving-average filter, explicit exits, costs and a separate evaluation period. This page supplies no measured trading edge.
Pro Tip: A candle or trend filter does not eliminate losses; compare the filtered rule against the baseline.
Momentum continuation
In strong uptrends, treat %R holding above -20 as evidence buyers keep paying up.
Dual confirmation with RSI
Study whether agreement between RSI and %R changes a predefined rule after costs. Their different formulas do not guarantee independent confirmation.
Failure swing style
Look for %R to fail at retesting a prior extreme while price makes a shallower wave—classic swing failure patterns.
Common Mistakes to Avoid
- Shorting every touch of -20 in a parabolic uptrend.
- Treating %R exactly like RSI despite different mathematics and sensitivity.
- Using tiny lengths on noisy tick charts without additional smoothing or filters.
- Ignoring gap risk when ranges compress and denominators shrink.
- Enabling shifted RSI plots without explaining the non-equivalent scale.
Frequently Asked Questions
Generate Williams %R Pine Script with Pineify
- Prototype OB/OS logic and alerts without manual formula boilerplate.
- Keep RSI companion studies documented and consistent.
- Review the exported version and test the script on the intended chart.
Disclaimer: Trading involves significant risk. The indicators, code, and strategies provided are for educational purposes only and do not constitute financial advice. Past performance is not indicative of future results.