Ultimate Oscillator (Larry Williams, 1976) — buying pressure as a
fraction of true range, averaged over three horizons at once and
weighted toward the shortest, bounded 0..100:
BP= close − min(low, prevClose) "buying pressure" TR=max(high, prevClose) − min(low, prevClose) A_n =ΣBP over n bars /ΣTR over n bars uo =100 · (4·A_short +2·A_medium + A_long) /7
Appends one column; undefined for the first longPeriod rows.
Williams built it against the complaint that a single-horizon oscillator
gives false divergences whenever the trader's horizon and the indicator's
disagree, so it reads three at once. The weights halve as the horizon
doubles, which is what keeps the three legs' contributions comparable
rather than letting the longest window dominate.
Reads high, low and close, each named by an option defaulting to its
DEFAULT_OHLCV column — the atr shape.
Three named periods, not a tuple
The three look-backs are shortPeriod / mediumPeriod / longPeriod,
not a periods: [7, 14, 28] array. A tuple would be shorter to type and
worse to read: the weights are positional (4 / 2 / 1), so
periods: [28, 14, 7] is a silently different indicator, while
longPeriod: 7 is obviously wrong at the call site. It also matches how
every other multi-horizon study here names its lengths
(macd's fastPeriod/slowPeriod, coppock's
longPeriod/shortPeriod/wmaPeriod). The three are validated as
strictly increasing for the same reason.
The weights themselves are not options: 4 / 2 / 1 is the definition,
and a weight vector nobody publishes an alternative for is a speculative
knob.
Definition source
TA-Lib's ULTOSC, matched bar-for-bar — the oracle asserts identical
null masks and agreement to 7.1e-15 at both (7, 14, 28) and
(3, 5, 9). The true range is the package's own trueRangeValues,
which the generator separately confirms is talib.TRANGEexactly
(0.0), so the ATR family and this study measure range the same way by
construction rather than by coincidence.
Edges
Warm-up is longPeriod rows, not longPeriod − 1: both BP and
TR read the previous close, so bar 0 has neither and the first
full window of longPeriod defined bars ends on bar longPeriod. The
two shorter legs are ready earlier and wait for the longest.
Bounded 0..100 on real bars, because 0 ≤ BP ≤ TR there: BP is
non-negative since close ≥ low, and TR ≥ close − min(low, prevClose)
since TR's upper term is max(high, prevClose) ≥ close. Point a
close option at a column that is not inside its bar (a smoothed
line, say) and both bounds can be left — reported honestly rather than
clamped, as the input is the thing that is inconsistent.
A window whose true range sums to zero → undefined, guarded
explicitly. On consistent bars it is the 0/0 of a completely flat
window; on a redirected close the numerator can be non-zero over a
zero range, and an ±Infinity in a chart's y-domain is worse than a
gap. Any one of the three legs being undefined makes the reading
undefined — a weighted mean of an unknown is unknown.
Scale- and shift-invariant: BP and TR are both homogeneous of
degree one in price and both unchanged by adding a constant to every
price, so their ratio is invariant to both. Pinned by property tests.
A leading gap shifts the start; an interior gap costs the windows
containing it — the gap bar and the longPeriod bars after it — after
which the study recovers. Note which bar a missing close costs: BP
and TR read the previous close, so a bar with no close removes both its
own reading and the next bar's, which is why the hole reaches one bar
further than the window alone would (trueRangeValues states the
same asymmetry).
Ultimate Oscillator (Larry Williams, 1976) — buying pressure as a fraction of true range, averaged over three horizons at once and weighted toward the shortest, bounded
0..100:Appends one column;
undefinedfor the firstlongPeriodrows.Williams built it against the complaint that a single-horizon oscillator gives false divergences whenever the trader's horizon and the indicator's disagree, so it reads three at once. The weights halve as the horizon doubles, which is what keeps the three legs' contributions comparable rather than letting the longest window dominate.
Reads high, low and close, each named by an option defaulting to its
DEFAULT_OHLCVcolumn — the atr shape.Three named periods, not a tuple
The three look-backs are
shortPeriod/mediumPeriod/longPeriod, not aperiods: [7, 14, 28]array. A tuple would be shorter to type and worse to read: the weights are positional (4 / 2 / 1), soperiods: [28, 14, 7]is a silently different indicator, whilelongPeriod: 7is obviously wrong at the call site. It also matches how every other multi-horizon study here names its lengths (macd'sfastPeriod/slowPeriod, coppock'slongPeriod/shortPeriod/wmaPeriod). The three are validated as strictly increasing for the same reason.The weights themselves are not options:
4 / 2 / 1is the definition, and a weight vector nobody publishes an alternative for is a speculative knob.Definition source
TA-Lib's
ULTOSC, matched bar-for-bar — the oracle asserts identical null masks and agreement to 7.1e-15 at both(7, 14, 28)and(3, 5, 9). The true range is the package's own trueRangeValues, which the generator separately confirms istalib.TRANGEexactly (0.0), so the ATR family and this study measure range the same way by construction rather than by coincidence.Edges
longPeriodrows, notlongPeriod − 1: bothBPandTRread the previous close, so bar 0 has neither and the first full window oflongPerioddefined bars ends on barlongPeriod. The two shorter legs are ready earlier and wait for the longest.0..100on real bars, because0 ≤ BP ≤ TRthere:BPis non-negative sinceclose ≥ low, andTR ≥ close − min(low, prevClose)sinceTR's upper term ismax(high, prevClose) ≥ close. Point acloseoption at a column that is not inside its bar (a smoothed line, say) and both bounds can be left — reported honestly rather than clamped, as the input is the thing that is inconsistent.undefined, guarded explicitly. On consistent bars it is the0/0of a completely flat window; on a redirectedclosethe numerator can be non-zero over a zero range, and an±Infinityin a chart's y-domain is worse than a gap. Any one of the three legs being undefined makes the reading undefined — a weighted mean of an unknown is unknown.BPandTRare both homogeneous of degree one in price and both unchanged by adding a constant to every price, so their ratio is invariant to both. Pinned by property tests.longPeriodbars after it — after which the study recovers. Note which bar a missing close costs:BPandTRread the previous close, so a bar with no close removes both its own reading and the next bar's, which is why the hole reaches one bar further than the window alone would (trueRangeValues states the same asymmetry).