Ultimate Oscillator (ULTOSC)
short_period = 7 | medium_period = 14 | long_period = 28 Overview
The Ultimate Oscillator combines buying pressure measurements across three different timeframes to reduce false signals inherent in single-period momentum indicators. Developed by Larry Williams, it calculates buying pressure relative to true range for short, medium, and long periods, then blends these values using a 4:2:1 weighting scheme that emphasizes recent activity while maintaining longer term context. This multi-timeframe approach helps the oscillator avoid the whipsaws and premature reversals that plague simpler momentum tools, providing more reliable overbought and oversold readings. Values range from 0 to 100, with readings above 70 typically indicating overbought conditions and below 30 suggesting oversold levels, though divergences between the oscillator and price often provide the most actionable signals. Traders appreciate how the indicator synthesizes different time perspectives into a single coherent momentum picture, making it easier to identify high probability reversal zones. Default parameters use periods of 7, 14, and 28 bars, representing fast, intermediate, and slow momentum components that together capture a comprehensive view of market strength.
Implementation Examples
Get started with ULTOSC in a few lines:
use vectorta::indicators::ultosc::{ultosc, UltOscInput, UltOscParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using with H/L/C slices
let high = vec![/* ... */];
let low = vec![/* ... */];
let close = vec![/* ... */];
let params = UltOscParams { timeperiod1: Some(7), timeperiod2: Some(14), timeperiod3: Some(28) };
let input = UltOscInput::from_slices(&high, &low, &close, params);
let result = ultosc(&input)?;
// Using with Candles (defaults: 7/14/28; sources "high","low","close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = UltOscInput::with_default_candles(&candles);
let result = ultosc(&input)?;
// Access the values
for v in result.values { println!("ULTOSC: {}", v); } API Reference
Input Methods ▼
// From H/L/C slices
UltOscInput::from_slices(&[f64], &[f64], &[f64], UltOscParams) -> UltOscInput
// From candles with custom sources
UltOscInput::from_candles(&Candles, &str, &str, &str, UltOscParams) -> UltOscInput
// From candles with defaults (high/low/close, 7/14/28)
UltOscInput::with_default_candles(&Candles) -> UltOscInput Parameters Structure ▼
pub struct UltOscParams {
pub timeperiod1: Option<usize>, // Default: 7
pub timeperiod2: Option<usize>, // Default: 14
pub timeperiod3: Option<usize>, // Default: 28
} Output Structure ▼
pub struct UltOscOutput {
pub values: Vec<f64>, // ULTOSC values (range 0..=100)
} Validation, Warmup & NaNs ▼
timeperiod1/2/3 > 0and must not exceed input length; otherwiseUltOscError::InvalidPeriods.- Finds the first index where both row
i-1andiare finite across H/L/C; earlier indices are ignored. - Warmup ends at
first_valid + max(timeperiod1, timeperiod2, timeperiod3) - 1; prior outputs areNaN. - If not enough valid data after warmup start, returns
UltOscError::NotEnoughValidData { needed, valid }. - If no consecutive finite rows exist, returns
UltOscError::AllValuesNaN. - Streaming:
update()returnsNoneuntil warmup completes or when any input isNaN.
Error Handling ▼
use vectorta::indicators::ultosc::{ultosc, UltOscError};
match ultosc(&input) {
Ok(output) => process(output.values),
Err(UltOscError::EmptyData) =>
eprintln!("Empty data provided"),
Err(UltOscError::InvalidPeriods { p1, p2, p3, data_len }) =>
eprintln!("Invalid periods: p1={}, p2={}, p3={}, len={}", p1, p2, p3, data_len),
Err(UltOscError::NotEnoughValidData { needed, valid }) =>
eprintln!("Need {} valid points after start, got {}", needed, valid),
Err(UltOscError::AllValuesNaN) =>
eprintln!("All values are NaN (or preceding row is NaN)"),
Err(e) => eprintln!("ULTOSC error: {}", e),
} Python Bindings
Basic Usage ▼
Calculate ULTOSC with NumPy arrays (defaults 7/14/28):
import numpy as np
from vectorta import ultosc
high = np.array([...], dtype=float)
low = np.array([...], dtype=float)
close = np.array([...], dtype=float)
# Defaults 7/14/28
values = ultosc(high, low, close)
# Custom params and kernel selection
values = ultosc(high, low, close, timeperiod1=7, timeperiod2=14, timeperiod3=28, kernel="auto")
print(values) # NumPy array in range [0, 100] Streaming Real-time Updates ▼
O(1) updates with UltOscStream:
from vectorta import UltOscStream
stream = UltOscStream(timeperiod1=7, timeperiod2=14, timeperiod3=28)
for h, l, c in hlc_feed:
v = stream.update(h, l, c)
if v is not None:
print("ULTOSC:", v) Batch Parameter Optimization ▼
Sweep three parameter ranges:
import numpy as np
from vectorta import ultosc_batch
high = np.array([...], dtype=float)
low = np.array([...], dtype=float)
close = np.array([...], dtype=float)
res = ultosc_batch(
high,
low,
close,
timeperiod1_range=(5, 9, 2),
timeperiod2_range=(12, 16, 2),
timeperiod3_range=(26, 30, 2),
kernel="auto"
)
print(res['values'].shape) # (rows, len)
print(res['timeperiod1'])
print(res['timeperiod2'])
print(res['timeperiod3']) CUDA Acceleration ▼
CUDA support for Ultimate Oscillator is coming soon. The API will follow existing CUDA-enabled patterns.
# Coming soon: CUDA-accelerated Ultimate Oscillator operations
# Similar shape and semantics to other CUDA batch APIs in this library. JavaScript/WASM Bindings
Basic Usage ▼
Calculate ULTOSC in JavaScript/TypeScript:
import { ultosc_js } from 'vectorta-wasm';
const high = new Float64Array([/* ... */]);
const low = new Float64Array([/* ... */]);
const close = new Float64Array([/* ... */]);
// timeperiod1, timeperiod2, timeperiod3
const values = ultosc_js(high, low, close, 7, 14, 28);
console.log('ULTOSC values:', values); Memory-Efficient Operations ▼
Use zero-copy operations for large datasets:
import { ultosc_alloc, ultosc_free, ultosc_into, memory } from 'vectorta-wasm';
const len = close.length;
const hPtr = ultosc_alloc(len);
const lPtr = ultosc_alloc(len);
const cPtr = ultosc_alloc(len);
const oPtr = ultosc_alloc(len);
// Copy H/L/C into WASM memory
new Float64Array(memory.buffer, hPtr, len).set(high);
new Float64Array(memory.buffer, lPtr, len).set(low);
new Float64Array(memory.buffer, cPtr, len).set(close);
// Compute directly into pre-allocated output
ultosc_into(hPtr, lPtr, cPtr, oPtr, len, 7, 14, 28);
const out = new Float64Array(memory.buffer, oPtr, len).slice();
// Free allocations
ultosc_free(hPtr, len); ultosc_free(lPtr, len);
ultosc_free(cPtr, len); ultosc_free(oPtr, len);
console.log('ULTOSC:', out); Batch Processing ▼
Test multiple parameter combinations:
import { ultosc_batch } from 'vectorta-wasm';
const cfg = {
timeperiod1_range: [5, 9, 2],
timeperiod2_range: [12, 16, 2],
timeperiod3_range: [26, 30, 2],
};
const res = ultosc_batch(high, low, close, cfg);
// res: { values: Float64Array, combos: Array<{timeperiod1?: number, timeperiod2?: number, timeperiod3?: number}>, rows: number, cols: number }
// Extract one row by params
const target = { timeperiod1: 7, timeperiod2: 14, timeperiod3: 28 };
const idx = res.combos.findIndex(c => c.timeperiod1 === target.timeperiod1 && c.timeperiod2 === target.timeperiod2 && c.timeperiod3 === target.timeperiod3);
if (idx >= 0) {
const start = idx * res.cols;
const row = res.values.slice(start, start + res.cols);
console.log('Selected ULTOSC row:', row);
} Performance Analysis
Across sizes, Rust CPU runs about 1.95× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05