Ultimate Oscillator

All functions · nseries package

WilliamsUltimateOscillator

func (s Series) WilliamsUltimateOscillator(h, l, c Series, short, mid, long int) Series

WilliamsUltimateOscillator is Larry Williams’s Ultimate Oscillator (1985): TA-Lib’s ULTOSC, Tulip’s ultosc, pandas-ta’s uo and TradingView’s Ultimate Oscillator. The name says whose oscillator it is: UltimateOscillator is Ehlers’s 2025 band-pass filter, a different indicator.

For each bar t >= 1, with the prior close cp = c[t-1], the buying pressure is bp = c[t] - min(l[t], cp) and the true range is tr = max(h[t], cp) - min(l[t], cp), the same bits as TrueRange(h, l, c) at that bar. For each of the three periods the ratio a = Sum(bp) / Sum(tr) is taken over the last short, mid and long bars, and the result is (4a_short + 2a_mid + a_long) * 100 / 7, on a 0 to 100 scale. The usual periods are 7, 14 and 28.

The receiver is unused, as in TrueRange: the data come from h, l and c, which must have equal lengths, otherwise the result is empty. The periods must satisfy 1 <= short <= mid <= long and long < len(c); the weights 4, 2 and 1 go with short, mid and long by position, and any other triple gives zeros. TA-Lib sorts its three periods, so its ULTOSC(28, 14, 7) equals its ULTOSC(7, 14, 28); here a descending triple returns zeros.

The warm-up reads 0, not NaN: bars 0 to long-1 are 0 whatever the data and the first valid index is long, since bar 0 has no prior close and the first window of long bars with one ends at bar long. Trim or mask the warm-up before use, since 0 is also a legitimate reading.

A window whose true ranges are all 0 contributes 0 to its ratio, so a flat market reads towards 0, not 50 (Tulip, TTR, TradingView and pandas-ta’s own uo give NaN there; with TA-Lib installed pandas-ta calls ULTOSC, which gives 0). The value lies in [0, 100] when every close lies within its bar’s range; closes outside the range, and highs below lows, are computed as they are.

NaN footprint: bar t >= 1 is bad when h[t], l[t], c[t] or c[t-1] is NaN or infinite, and a valid bar is NaN when any bar of its long window is bad or when one of the six window sums overflows. So a non-finite h[p] or l[p] with p >= 1 gives NaN on bars max(p, long) to min(p+long-1, len(c)-1), and a non-finite c[p] on bars max(p, long) to min(p+long, len(c)-1), as it is also bar p+1’s prior close; h[0] and l[0] are not read. The result recovers once the bad bar leaves the long window. A ratio that overflows from finite sums is passed through.

Recipe: on finite data, from bar long, with

tr := c.TrueRange(h, l, c).SetN(1, 0)
bp := c.Sub(l).Add(l.Sub(c.Shift(1)).Max(0.0)).SetN(1, 0)

the result equals

bp.Sum(short).Ratio(tr.Sum(short)).Mul(4.0).
    Add(bp.Sum(mid).Ratio(tr.Sum(mid)).Mul(2.0)).
    Add(bp.Sum(long).Ratio(tr.Sum(long))).
    Mul(100.0 / 7)

bit for bit, apart from the sign of a zero and barring overflow of the window sums, when each low lies within a factor of two of its close and of the prior close, so that the two buying-pressure forms agree. The cost is O(1) amortised per bar.