Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Fintech Wilder RMA — SMA-Seeded 1/n Smoothing Algorithm

A canonical, well-specified, cross-language (Python + TypeScript) reference implementation of Wilder's moving average (RMA / SMMA) — the SMA-seeded alpha = 1/period smoother behind RSI, ATR, and ADX — with a streaming online engine and Yahoo Finance test cases.

Python TypeScript License Tests

📖 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 IndicatorsTrend Smoothing ↔️ Family siblings: SMA · EMA · WMA.

Catalog topic D07-F01-A04
Domain D07 — Technical Indicators
Family D07-F01 — Trend Smoothing
Difficulty 2 / 5
Languages Python, TypeScript
Powers RSI, ATR, ADX

Table of contents


What is Wilder's RMA?

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.

RMA vs EMA

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.

Why this implementation

  • Canonical SMA seed and 1/period recurrence — 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).

Install

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 Finance

Quickstart

Python

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)   # 27

TypeScript

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); // 27

Streaming (live feeds)

StreamingRMA 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())

Yahoo Finance use cases

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.

The mathematics

  • 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 toward c.
  • Span equivalence: α = 1/n = 2/(span+1)span = 2n − 1.
  • Period = 1: the identity, RMA_t = x_t.

Worked example (exact)

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.

API reference

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

Edge cases & limitations

  • 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 period observations 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.

Testing

Python (31 tests; live Yahoo test deselected by default)

cd python && pip install -e ".[dev]" && pytest

TypeScript (21 tests, zero runtime dependencies)

cd typescript && npm install && npm test && npm run build

Related algorithms

  • D07-F01-A01SMA (supplies the seed)
  • D07-F01-A02EMA (α = 2/(n+1))
  • D07-F01-A03WMA (linear weights)
  • Wilder's RMA is the smoothing inside RSI, ATR, and ADX.

Full index: Awesome FinTech Algorithms.

License

MIT © The Fintech Builder. Part of the 100 FinTech Algorithms library.

Releases

Packages

Contributors

Languages