A canonical, well-specified, cross-language (Python + TypeScript) reference implementation of Wilder's moving average (RMA / SMMA) — the SMA-seeded
alpha = 1/periodsmoother behind RSI, ATR, and ADX — with a streaming online engine and Yahoo Finance test cases.
📖 Full article (canonical): Wilder RMA — The Fintech Builder
This repository is the runnable, production-oriented companion to that article. The article teaches the concept; this repo is the code you install and build on.
🧭 Browse all algorithms: Awesome FinTech Algorithms — the full index of the library.
🗂️ This algorithm's domain: Technical Indicators › Trend Smoothing
| Catalog topic | D07-F01-A04 |
| Domain | D07 — Technical Indicators |
| Family | D07-F01 — Trend Smoothing |
| Difficulty | 2 / 5 |
| Languages | Python, TypeScript |
| Powers | RSI, ATR, ADX |
- What is Wilder's RMA?
- RMA vs EMA
- Why this implementation
- Install
- Quickstart
- Streaming (live feeds)
- Yahoo Finance use cases
- The mathematics
- Worked example (exact)
- API reference
- Edge cases & limitations
- Testing
- Related algorithms
- License
Wilder's moving average — also called RMA or SMMA (smoothed moving
average) — is a recursive smoother introduced by J. Welles Wilder. It is seeded
with the simple average of the first period values, then each later state moves
1/period of the remaining distance toward the current value:
RMA_period = mean(x_1 … x_period) (seed)
RMA_t = RMA_{t-1} + (x_t − RMA_{t-1}) / period (t > period)
That 1/period gain (not the EMA's 2/(period+1)) is exactly the smoothing
used inside RSI, ATR, and ADX — which is why matching it precisely
matters if you want indicator values that agree with the standard references.
The first period − 1 outputs are null.
Both are SMA-seeded recursive smoothers; only the gain differs:
| smoothing constant | period 14 behaves like | |
|---|---|---|
| Wilder RMA (this repo) | α = 1 / period |
EMA of span 27 |
| EMA | α = 2 / (period + 1) |
EMA of span 14 |
So a Wilder RMA of period n is equivalent to an EMA of span 2n − 1 — it is
noticeably slower and smoother than an EMA of the same period. This package
exposes equivalent_ema_span(period) for exactly that conversion.
- Canonical SMA seed and
1/periodrecurrence — matches the values used by RSI/ATR/ADX references, not an approximation. - Full-precision recursive state — internal state is never rounded.
- Strict validation — missing / non-finite values are rejected; inputs are never mutated.
- Explicit span equivalence (
equivalent_ema_span) so you can reason about RMA and EMA on the same footing. - A streaming engine (
StreamingRMA) that yields the identical numbers as the batch kernel, one observation at a time, with checkpoint/restore. - Cross-language parity — Python and TypeScript assert the same acceptance
fractions (
35/3, 115/9, 356/27, 1198/81).
Python
pip install fintech-wilder-rma # core, zero dependencies
pip install "fintech-wilder-rma[yahoo]" # + live Yahoo Finance (yfinance)TypeScript / JavaScript (Node ≥ 20)
npm install fintech-wilder-rma
npm install yahoo-finance2 # optional, for live Yahoo FinancePython
from fintech_rma import rma, StreamingRMA, equivalent_ema_span
rma([10, 13, 12, 15, 14, 18], period=3)
# [None, None, 11.666…, 12.777…, 13.185…, 14.790…]
equivalent_ema_span(14) # 27TypeScript
import { rma, StreamingRMA, equivalentEmaSpan } from "fintech-wilder-rma";
rma([10, 13, 12, 15, 14, 18], 3);
// [null, null, 11.666…, 12.777…, 13.185…, 14.790…]
equivalentEmaSpan(14); // 27StreamingRMA holds O(1) state and accepts one tick at a time — ideal for live
RSI/ATR/ADX pipelines and dashboards. Same numbers as the batch kernel;
checkpointable for restart and correction recovery.
from fintech_rma import StreamingRMA
atr_smoother = StreamingRMA(period=14)
for true_range in feed:
value = atr_smoother.update(true_range) # None during warm-up, then the RMA
if atr_smoother.ready:
publish(value)
resumed = StreamingRMA.from_state_dict(atr_smoother.state_dict())Real-data demos, kept separate from the test path so CI never flakes:
- Unit tests run offline against a committed synthetic OHLCV fixture in
the Yahoo schema (
Date,Open,High,Low,Close,Adj Close,Volume). - Live downloads are opt-in via the optional dependency (
yfinance/yahoo-finance2).
from fintech_rma import rma, load_close_series, fetch_close_series
closes = load_close_series("data.csv") # offline, prefers "Adj Close"
rma(closes, period=14)
closes = fetch_close_series("AAPL", period="6mo") # live (opt-in)
rma(closes, period=14)Data note: the committed fixtures are synthetic and exist only to exercise the load → RMA path. They are not real market observations.
- Seed:
RMA_n = (x_1 + … + x_n) / n. - Recurrence:
RMA_t = RMA_{t-1} + (x_t − RMA_{t-1}) / n. - Constant
c:RMA_{t+k} = c + (1 − 1/n)^k (RMA_t − c)— the gap decays geometrically towardc. - Span equivalence:
α = 1/n = 2/(span+1)⇒span = 2n − 1. - Period = 1: the identity,
RMA_t = x_t.
Period 3 ⇒ α = 1/3, SMA seed. Input: 10, 13, 12, 15, 14, 18.
| # | value | RMA | status |
|---|---|---|---|
| 1 | 10 | null |
warming |
| 2 | 13 | null |
warming |
| 3 | 12 | 35/3 = 11.6666… |
ready (seed) |
| 4 | 15 | 115/9 = 12.7777… |
ready |
| 5 | 14 | 356/27 = 13.1851… |
ready |
| 6 | 18 | 1198/81 = 14.7901… |
ready |
Step 4: RMA₄ = 35/3 + (15 − 35/3)/3 = 35/3 + 10/9 = 115/9. These exact
fractions are the shared acceptance values asserted by both language suites.
| Purpose | Python | TypeScript |
|---|---|---|
| Wilder RMA | rma(values, period) |
rma(values, period) |
| Smoothing constant | alpha_from_period(period) |
alphaFromPeriod(period) |
| Equivalent EMA span | equivalent_ema_span(period) |
equivalentEmaSpan(period) |
| Streaming engine | StreamingRMA(period) |
new StreamingRMA(period) |
| Yahoo (offline) | load_close_series(src) |
loadCloseSeries(path) |
| Yahoo (live) | fetch_close_series(symbol) |
fetchCloseSeries(symbol) |
| Errors | RMAValidationError |
RMAValidationError |
- Slower than EMA: at the same period, RMA lags more (span
2n − 1). Don't compare an RMA(14) to an EMA(14) as if they were the same speed. - Seed dependence: the SMA seed travels with the data; a different seed rule changes early values.
- Warm-up: no value until
periodobservations exist. - Revisions: for
period > 1, correcting a past value changes every later RMA value; recompute from the start or a verified checkpoint. - Missing data: handle gaps upstream — this kernel rejects non-finite values.
Python (31 tests; live Yahoo test deselected by default)
cd python && pip install -e ".[dev]" && pytestTypeScript (21 tests, zero runtime dependencies)
cd typescript && npm install && npm test && npm run buildD07-F01-A01— SMA (supplies the seed)D07-F01-A02— EMA (α = 2/(n+1))D07-F01-A03— WMA (linear weights)- Wilder's RMA is the smoothing inside RSI, ATR, and ADX.
Full index: Awesome FinTech Algorithms.
MIT © The Fintech Builder. Part of the 100 FinTech Algorithms library.