Williams %R (WILLR)
period = 14 Overview
Larry Williams developed the %R indicator in 1973 as a momentum oscillator that reveals where the current close sits within the recent trading range, inverting the stochastic oscillator concept to run from 0 to -100 rather than 0 to 100. The indicator calculates how far the current close is from the highest high over the lookback period, expressing this as a percentage of the total range between the highest high and lowest low. Williams designed %R to identify overbought conditions when readings approach zero, indicating prices are near the top of their recent range, and oversold conditions when readings approach -100, showing prices near the bottom. His indicator gained credibility through Williams' own trading success, including his legendary 1987 World Cup Championship performance where he turned $10,000 into over $1.1 million in twelve months. The %R excels at spotting potential reversal points because it responds quickly to price changes while maintaining enough smoothness to filter out minor fluctuations. Williams emphasized watching for failures of %R to reach extreme levels during trends, as these failures often signal weakening momentum before price reversals become apparent, making the indicator particularly effective for timing market turns when combined with price pattern analysis.
Implementation Examples
Compute Williams' %R from slices or candles:
use vectorta::indicators::willr::{willr, WillrInput, WillrParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using with high/low/close slices
let high = vec![1.0, 2.0, 3.0, 4.0];
let low = vec![0.5, 1.5, 2.5, 3.5];
let close = vec![0.75, 1.75, 2.75, 3.75];
let params = WillrParams { period: Some(14) }; // Default is 14
let input = WillrInput::from_slices(&high, &low, &close, params);
let result = willr(&input)?;
// Using with Candles (defaults: period=14; sources high/low/close)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = WillrInput::with_default_candles(&candles);
let result = willr(&input)?;
// Access values
for value in result.values {
println!("%R: {}", value);
} API Reference
Input Methods ▼
// From high/low/close slices
WillrInput::from_slices(&[f64], &[f64], &[f64], WillrParams) -> WillrInput
// From candles (uses high/low/close sources)
WillrInput::from_candles(&Candles, WillrParams) -> WillrInput
// With default parameters (period=14)
WillrInput::with_default_candles(&Candles) -> WillrInput Parameters Structure ▼
#[derive(Debug, Clone)]
pub struct WillrParams {
pub period: Option<usize>, // Default: 14
} Output Structure ▼
#[derive(Debug, Clone)]
pub struct WillrOutput {
pub values: Vec<f64>, // %R values (-100..0). Warmup indices are NaN.
} Validation, Warmup & NaNs ▼
period > 0andperiod ≤ len; otherwiseWillrError::InvalidPeriod.- Finds first index with finite H/L/C; requires at least
periodvalid points after that orWillrError::NotEnoughValidData. - Warmup region (before
first_valid + period - 1) is filled withNaN. - If a window’s
high == low, the value is0.0; any NaN in the window yieldsNaN.
Error Handling ▼
use vectorta::indicators::willr::{willr, WillrError, WillrInput, WillrParams};
let input = WillrInput::from_slices(&high, &low, &close, WillrParams { period: Some(14) });
match willr(&input) {
Ok(out) => process(out.values),
Err(WillrError::AllValuesNaN) => eprintln!("All input values are NaN"),
Err(WillrError::InvalidPeriod { period, data_len }) => eprintln!("Invalid period: {} (len={})", period, data_len),
Err(WillrError::NotEnoughValidData { needed, valid }) => eprintln!("Not enough valid data: needed={}, valid={}", needed, valid),
Err(WillrError::EmptyOrMismatched) => eprintln!("Empty or mismatched input slices"),
Err(WillrError::OutputLenMismatch { .. }) => unreachable!(),
} Python Bindings
Basic Usage ▼
Calculate Williams' %R using NumPy arrays (period default 14):
import numpy as np
from vectorta import willr
# Prepare H/L/C arrays as float64
high = np.array([1.0, 2.0, 3.0, 4.0], dtype=np.float64)
low = np.array([0.5, 1.5, 2.5, 3.5], dtype=np.float64)
close = np.array([0.75, 1.75, 2.75, 3.75], dtype=np.float64)
# With explicit period and optional kernel ("auto", "scalar", "avx2", ...)
values = willr(high, low, close, period=14, kernel="auto")
print(values) Streaming Real-time Updates ▼
from vectorta import WillrStream
stream = WillrStream(period=14)
for h, l, c in hlc_feed:
wr = stream.update(h, l, c)
if wr is not None:
handle(wr) Batch Parameter Sweep ▼
import numpy as np
from vectorta import willr_batch
high = np.array([...], dtype=np.float64)
low = np.array([...], dtype=np.float64)
close = np.array([...], dtype=np.float64)
res = willr_batch(high, low, close, period_range=(5, 30, 5), kernel="auto")
values = res["values"] # shape: (num_periods, len)
periods = res["periods"] # list of periods used
print(values.shape, periods) CUDA Acceleration ▼
GPU-accelerated batch (device arrays, f32 inputs):
import numpy as np
from vectorta import willr_cuda_batch_dev
high = np.array([...], dtype=np.float32)
low = np.array([...], dtype=np.float32)
close = np.array([...], dtype=np.float32)
gpu_out = willr_cuda_batch_dev(high, low, close, period_range=(5, 30, 1), device_id=0)
# gpu_out is a device array wrapper; convert/copy as needed in your environment JavaScript/WASM Bindings
Basic Usage ▼
Compute Williams' %R in JS/TS:
import { willr_js } from 'vectorta-wasm';
const high = new Float64Array([/* ... */]);
const low = new Float64Array([/* ... */]);
const close = new Float64Array([/* ... */]);
const values = willr_js(high, low, close, 14);
console.log('%R:', values); Memory-Efficient Operations ▼
Zero-copy into WASM memory for large arrays:
import { willr_alloc, willr_free, willr_into, memory } from 'vectorta-wasm';
const len = close.length;
const hPtr = willr_alloc(len);
const lPtr = willr_alloc(len);
const cPtr = willr_alloc(len);
const outPtr = willr_alloc(len);
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 in-place: (high_ptr, low_ptr, close_ptr, out_ptr, len, period)
willr_into(hPtr, lPtr, cPtr, outPtr, len, 14);
const out = new Float64Array(memory.buffer, outPtr, len).slice();
willr_free(hPtr, len); willr_free(lPtr, len);
willr_free(cPtr, len); willr_free(outPtr, len);
console.log(out); Batch Processing ▼
Sweep periods and get full result matrix:
import { willr_batch } from 'vectorta-wasm';
const cfg = { period_range: [5, 30, 5] };
const out = willr_batch(high, low, close, cfg);
// out: { values: Float64Array, combos: { period?: number }[], rows: number, cols: number }
console.log(out.rows, out.cols, out.combos); Performance Analysis
Across sizes, Rust CPU runs about 1.16× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05