Files
wickra/docs/wiki/indicators/momentum/Indicator-WilliamsR.md
T
kingchenc 278b6afaa4 E4: commit the documentation sources
The 33 Markdown files under docs/wiki/ were never tracked. Commit them
into the repository so the documentation is versioned alongside the
code: 8 top-level pages plus 25 per-indicator deep dives under
indicators/{momentum,trend,volatility,volume}/.

The pages are kept in-repo (not pushed to a flat GitHub Wiki), so the
relative indicators/<family>/... links in Home.md resolve correctly
when rendered on GitHub.
2026-05-22 16:18:04 +02:00

185 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WilliamsR
> Williams %R — Larry Williams' negated mirror of fast Stochastic %K,
> plotted on `[100, 0]` instead of `[0, 100]`.
## Quick reference
| Field | Value |
|-------|-------|
| Family | Momentum |
| Sub-category | bounded oscillator |
| Input type | `Candle` |
| Output type | `f64` |
| Output range | `[100, 0]` |
| Default parameters | `period = 14` (Python) |
| Warmup period | `period` (14 for `period = 14`) |
| Interpretation | overbought above `20`, oversold below `80` |
## Formula
For each new candle, let `HH` and `LL` be the highest high and lowest
low over the last `period` candles:
```
HH_t = max(high_{t-period+1}, …, high_t)
LL_t = min(low_{t-period+1}, …, low_t)
%R_t = 100 · (HH_t close_t) / (HH_t LL_t) when HH ≠ LL
%R_t = 50 when HH == LL (flat range)
```
This is the negation of fast Stochastic `%K` measured from the *top* of
the window: when the close sits at the window high, `%R = 0`; when it
sits at the window low, `%R = 100`.
## Parameters
| Name | Type | Default (Python) | Valid range | Description |
|------|------|------------------|-------------|-------------|
| `period` | `usize` | `14` | `>= 1` | Lookback window for the `HH` / `LL` extrema. |
`WilliamsR::new(0)` returns `Error::PeriodZero`.
## Inputs / Outputs
From `impl Indicator for WilliamsR`:
```rust
type Input = Candle;
type Output = f64;
fn update(&mut self, candle: Candle) -> Option<f64>;
```
Python's `WilliamsR.batch(high, low, close)` returns a 1-D `float64`
`np.ndarray` (warmup → `NaN`). Node's `WilliamsR.batch(high, low, close)`
returns a flat `number[]` (warmup → `NaN`); only `batch` is exposed on
the Node binding.
## Warmup
`warmup_period()` returns `period`. Williams %R works on a rolling
range, not a rolling diff, so once `period` candles have arrived the
indicator is ready — there is no off-by-one. The first `period 1`
calls to `update()` return `None`; the `period`-th call returns the
first `Some(value)`.
## Edge cases
- **Close at the window high.** `%R == 0` exactly. The unit test
`close_at_high_yields_zero` pins this case (with H, L = 8, 10, 12 and
closes ending at 12, the result is `0`). Note that floating-point
zero can print as `-0` when scaled by `-100`; both compare equal to
`0`.
- **Close at the window low.** `%R == 100` exactly (test
`close_at_low_yields_minus_100`).
- **Flat range.** When `HH == LL`, the implementation returns `50` as
the neutral convention.
- **Reset.** `reset()` clears the candle buffer; the next `period`
updates return `None`.
## Examples
### Rust
```rust
use wickra::{BatchExt, Candle, Indicator, WilliamsR};
let candles = vec![
Candle::new(9.0, 10.0, 8.0, 9.0, 1.0, 0).unwrap(),
Candle::new(10.0, 11.0, 9.0, 10.0, 1.0, 0).unwrap(),
Candle::new(12.0, 12.0, 10.0, 12.0, 1.0, 0).unwrap(), // close == HH
];
let mut w = WilliamsR::new(3)?;
let out = w.batch(&candles);
println!("Williams %R(3) at idx 2 = {}", out[2].unwrap());
# Ok::<(), wickra::Error>(())
```
Verified output:
```
Williams %R(3) at idx 2 = -0
```
(`-0.0` is bit-equal to `0.0` in IEEE-754; the negative sign is just a
side effect of multiplying `+0.0` by `-100.0`.)
### Python
```python
import numpy as np
import wickra as ta
high = np.array([10.0, 11.0, 12.0])
low = np.array([8.0, 9.0, 10.0])
close = np.array([9.0, 10.0, 12.0])
w = ta.WilliamsR(3)
out = w.batch(high, low, close)
print('warmup:', w.warmup_period())
print('row 2 :', out[2])
```
Verified output:
```
warmup: 3
row 2 : -0.0
```
### Node
```javascript
const wickra = require('wickra');
const high = [10.0, 11.0, 12.0];
const low = [8.0, 9.0, 10.0];
const close = [9.0, 10.0, 12.0];
const w = new wickra.WilliamsR(3);
const out = w.batch(high, low, close);
console.log('row 2:', out[2]);
```
Verified output:
```
row 2: -0
```
## Interpretation
- **Larry Williams' thresholds.** `%R > 20` is overbought; `%R < 80`
is oversold. Because the scale runs from `100` (oversold) to `0`
(overbought), the inequalities feel inverted to anyone used to
Stochastic — but the *positions* of the bands are identical.
- **Failure swings.** A `%R` value that pokes into overbought, retreats,
then fails to reach overbought on the next rally is the classic
Williams "failure swing" — interpreted as bearish exhaustion.
- **Use alongside trend.** %R is a pure range oscillator; in a strong
trend it can stay pinned at `0` or `100` for many bars. Pair with
ADX or a moving-average filter before reading it as a reversal cue.
## Common pitfalls
- **Sign inversion.** Williams %R lives in `[100, 0]`, not `[0, 100]`.
Code that assumes "higher value = more bullish" will work; code that
assumes a positive range will silently mis-classify every value.
- **Mirror of fast %K, not slow.** Williams %R has no built-in
smoothing; it tracks raw `%K` (with a sign flip and a shift). If you
need a smoothed version, drive `%R` through your own `Sma` or `Ema`
via a `Chain`.
## References
- Larry Williams, *How I Made One Million Dollars … Last Year …
Trading Commodities*, Windsor Books, 1973 — the original %R
publication.
## See also
- [Indicator: Stochastic](Indicator-Stochastic.md) — the positive-axis
sibling; `%R` and `%K` are linked by `%R = %K 100`.
- [Indicator: Rsi](Indicator-Rsi.md) — slower bounded oscillator,
better behaved in trending markets.
- [Warmup Periods](../../Warmup-Periods.md) — bare `period` (no off-by-one).