Suppose two systems both report a “14-period Wilder average,” yet their first values disagree and the difference persists. The recurrence may be correct in both systems. The mismatch can come from the seed, warm-up alignment, missing rows, or rounded state.
That is why Wilder's Moving Average—often called RMA, Wilder smoothing, a modified moving average, or sometimes SMMA—needs a complete contract rather than a one-line formula.
In this tutorial we will calculate an SMA-seeded RMA, implement it in Python and TypeScript, compare it with an equal-period EMA, and test the operational details that determine reproducibility. The method is historically associated with J. Welles Wilder's 1978 indicator systems; the institutional record for his original book establishes that source context.
What RMA gives a practitioner and a builder
For a practitioner, RMA provides a smoother causal view of an observed series. For a builder, it provides an -state recurrence used inside later indicators. Official TC2000 methodology identifies Wilder smoothing in both Wilder's RSI and traditional ATR.
By the end, you will be able to:
- derive alpha from the declared period;
- distinguish warm-up, seed, and recursive updates;
- explain why a period-14 RMA is not a period-14 EMA;
- preserve full-precision state across updates and restarts;
- verify two language implementations against one fixture.
The only mathematical prerequisite is the Simple Moving Average (SMA), which we use once to initialize the state.
Intuition: keep most of yesterday, accept a fraction of today
Let be the prior RMA and the current observation. With period , the update is:
Read the formula from the inside out:
- Measure the gap between the current value and the prior state.
- Take one -th of that gap.
- Add the adjustment to the prior state.
The equivalent weighted form is:
Those coefficients are non-negative and sum to one. The new state must therefore lie between the prior state and current value. That convex-bound property is useful in tests.
See one exact update
In the canonical example, the prior state is , the current value is , and the period is . Notice that the new state moves exactly one third of the gap:
The full-precision calculation is:
Only the label is rounded. The fraction—or its full binary floating-point equivalent—continues into the next update.
Initialization is part of the algorithm
The recurrence needs a prior state. Our canonical finance-style policy waits for valid observations, then uses their SMA:
Outputs before observation are null. After the seed exists, every new observation advances the recurrence once.
This is consistent with maintained classic Wilder implementations.
TA-Lib's RSI source
accumulates an initial period average and describes subsequent Wilder updates
as multiplying the prior value by period - 1, adding today's value, and
dividing by period.
A first-value seed is also possible:
It starts immediately but produces a different early path. Neither choice should be hidden behind a library default. Period, seed, warm-up alignment, and reset policy must travel with the result.
RMA versus EMA: the period labels are not interchangeable
A common finance-style EMA converts span to:
RMA uses:
At equal labels , the RMA alpha is smaller. It reacts more slowly. To give an EMA the same alpha as an -period RMA:
The TC2000 ATR methodology documents this conversion. A 14-period RMA recurrence matches an EMA alpha commonly labeled span 27—but only when the seed and output alignment also match.
The following visual holds the period label and SMA seed constant. The RMA uses alpha , while the EMA uses alpha :
Both begin at . By observation six, the RMA is , or 14.7901, while the equal-period EMA is , or 15.8333.
The complete data contract
A numeric array is sufficient for a small function, but a reproducible financial series needs more:
| Concern | Canonical rule |
|---|---|
| Ordering | Unique, strictly increasing timestamp or sequence key |
| Value | Finite number; zero is valid |
| Identity | Stable series identifier |
| Unit | Stable currency, range, return points, count, or other declared unit |
| Basis | One adjustment, FX, inflation, or constituent methodology |
| Period | Positive integer |
| Seed | SMA of first period observations |
| Missing value | Reject; never silently turn into zero |
| Missing row | No update in observation-space mode |
| Rounding | Presentation only |
| Correction | Replay the dependent suffix |
Fixed-alpha RMA decays per accepted observation, not per wall-clock hour. An overnight gap and a one-minute gap both represent one update if each contributes one row. A time-aware smoother is a different algorithm.
Similarly, mixing adjusted and unadjusted prices inside one state history is invalid. The resulting jump is a data-basis break, not market movement.
Walk through the canonical fixture
Use period 3 and the synthetic values:
| Observation | Value | Exact RMA | Display | State |
|---|---|---|---|---|
| 1 | 10 | null | — | Warming |
| 2 | 13 | null | — | Warming |
| 3 | 12 | 11.6667 | SMA seed | |
| 4 | 15 | 12.7778 | Recursive | |
| 5 | 14 | 13.1852 | Recursive | |
| 6 | 18 | 14.7901 | Recursive |
The first two values only contribute to the seed sum. Observation three creates the first numeric state. Each later state depends on the prior full-precision RMA.
Use the interactive causal playground to step through the same values. Change the period to alter alpha and warm-up, then switch to first-value initialization to see which differences come from the seed rather than the recurrence. The static comparison above remains the complete fallback when scripts or motion are unavailable.
A real index window: observations versus derived arithmetic
A historical example is useful here only if its evidence boundary is explicit. Nasdaq Trader's year-to-date file reports these Nasdaq Composite Index closing values. The close column is observed provider data. The RMA and EMA columns are author-derived from those six values with period 3, the same three-observation SMA seed, one update per listed session, and no intermediate rounding.
| Session date | Nasdaq observed close | Derived RMA, alpha 1/3 | Derived equal-period EMA, alpha 1/2 |
|---|---|---|---|
| 2026-03-02 | 22,748.86 | — | — |
| 2026-03-03 | 22,516.69 | — | — |
| 2026-03-04 | 22,807.48 | 22,691.0100 | 22,691.0100 |
| 2026-03-05 | 22,748.99 | 22,710.3367 | 22,720.0000 |
| 2026-03-06 | 22,387.68 | 22,602.7844 | 22,553.8400 |
| 2026-03-09 | 22,695.95 | 22,633.8396 | 22,624.8950 |
The common seed is 22,691.01. When the observed close falls to 22,387.68 on 2026-03-06, the equal-period EMA moves farther because alpha (1/2) is larger than the RMA's (1/3). Both then move back toward the 2026-03-09 observation. This visual demonstrates smoothing mechanics; it does not show causation, predictive value, indicator superiority, or a tradable strategy. The extract does not establish the file's historical publication timestamp or revision chain.
Provider context: Nasdaq Trader Daily Market Files, Nasdaq data definitions, and the 2026 year-to-date CSV, retrieved 2026-07-22T23:23:41Z.
Translate the contract into code
The reference Python core is intentionally small:
alpha = 1.0 / period
seed_sum = 0.0
rma = None
for observation, value in enumerate(numbers, start=1):
if observation <= period:
seed_sum += value
if observation < period:
output.append(None)
elif observation == period:
rma = seed_sum / period
output.append(rma)
else:
rma += alpha * (value - rma)
output.append(rma)
The TypeScript implementation follows the same state transitions:
const alpha = 1 / period;
let seedSum = 0;
let rma: number | null = null;
numbers.forEach((value, zeroBasedIndex) => {
const observation = zeroBasedIndex + 1;
if (observation <= period) seedSum += value;
if (observation < period) output.push(null);
else if (observation === period) {
rma = seedSum / period;
output.push(rma);
} else {
rma = rma + alpha * (value - rma);
output.push(rma);
}
});
The delivered source adds validation around these blocks. Booleans are
rejected even though Python treats bool as an integer subtype. NaN, infinity,
null, non-integer periods, and non-positive periods are also invalid.
Test behavior, not just example values
Both languages consume the same JSON fixture. The tests verify the canonical path within , but example parity alone is not enough.
Useful invariants include:
- Period one is identity: alpha equals one, so output equals input.
- Constant preservation: a constant series remains constant after seed.
- Convex bounds: every update lies between prior state and current input.
- Scale and translation equivariance: applying with positive before smoothing gives .
- Non-mutation: calculation does not alter the source array.
- Warm-up alignment: a short series returns only null values.
These tests establish implementation correctness. They do not show that an RMA crossover predicts prices, survives transaction costs, or generalizes out-of-sample.
Operational failure modes
Rounding recursive state
If each displayed value is rounded and then reused, the error compounds. Persist and recurse with full precision.
Treating a missing row as zero
Zero is a real observation. Substituting it for missing data introduces a large artificial move. Reject or apply an explicit upstream imputation policy.
Correcting history without replay
A correction changes every dependent state. If changes by , the immediate RMA changes by . After unchanged updates, the remaining difference is:
Replay from the start or from a verified checkpoint before the correction.
Comparing vendor labels without seed details
RMA, SMMA, modified moving average, and Wilder smoothing may share the recurrence while differing in initialization or indexing. Compare alpha, seed, warm-up, and missing-data rules—not the label alone.
Reading smoothness as prediction
RMA is causal and lagging. Its visual stability is not evidence of future returns or a profitable signal.
Where to use RMA
RMA is appropriate when you need:
- Wilder-smoothed gains and losses for RSI;
- Wilder-smoothed true range for ATR;
- Wilder-smoothed directional movement for DMI and ADX;
- a simple causal baseline with constant memory;
- parity with a platform whose RMA conventions are explicit.
For each published output, include the source identity, unit, basis, period, alpha, seed, missing policy, and rounding policy.
Summary and next topic
Wilder RMA is a fixed-alpha recursive smoother with . Its canonical first state is an -observation SMA, and its period is not a hard window. Equal-period RMA and EMA differ; an EMA recurrence matches RMA only when alpha, seed, and alignment all match.
You can now calculate, implement, and verify the smoothing engine. The natural next tutorial is RSI, where the same recurrence is applied separately to gains and losses before their relative-strength ratio is mapped to a bounded oscillator.
Asset map
| Asset ID | Topic section | Article placement | Video scene | Source path | Static fallback | Status |
|---|---|---|---|---|---|---|
| RMA-V01 | Algorithm lifecycle | Initialization | 3 | visuals/mermaid/calculation-flow.md | Pseudocode | Ready |
| RMA-V02 | One recursive update | Intuition | 4 | visuals/static/rma-step.svg | Same asset | Ready |
| RMA-V03 | RMA versus EMA | Comparison | 6 | visuals/static/rma-vs-ema.svg | Same asset | Ready |
| RMA-V04 | Worked example and variants | Canonical fixture | 2, 3, 5, 6 | visuals/animated/rma-playground.html | visuals/static/rma-vs-ema.svg | Ready |
| RMA-V05 | Bounded historical mechanics | Historical example | 6 | visuals/static/nasdaq-composite-rma-example.svg | Historical table | Ready |
| RMA-CODE | Implementation | Code walkthrough | 7 | implementations/python/rma.py, implementations/typescript/rma.ts | Article code blocks | Ready |
| RMA-TEST | Validation | Tests | 8 | tests/test_rma.py, tests/rma.test.ts | Worked table | Ready |
Primary references
- Wilder's original 1978 book record
- TA-Lib classic RSI source
- TC2000 Wilder RSI methodology
- TC2000 ATR methodology
This tutorial is educational and does not provide investment advice.
Rendered from the canonical Mermaid sources linked by this article.
Wilder RMA calculation flow
This diagram separates the finite warm-up from the causal recursive state.
The first numeric output is the simple-average seed. Every later output uses only prior state and the current observation, so no future data is revealed.
Takeaway: warm-up, initialization, and recurrence are three distinct states of one causal algorithm.
ReferencesPrimary sources and evidence notesExpand the source trail, evidence role, and limitations behind the engineering choices.
Expand the source trail, evidence role, and limitations behind the engineering choices.
Access date for methodology sources: 2026-07-20. Nasdaq market-statistics sources were retrieved at 2026-07-22T23:23:41Z. The executable fixture remains synthetic; the article also contains one small, attributed historical extract.
RMA-01 — New Concepts in Technical Trading Systems
- Organization or authors: J. Welles Wilder
- Source type: Original monograph; institutional repository record
- Publication or effective date: 1978
- Version: Trend Research, 141 pages, ISBN 0894590278
- URL or DOI: University of Macedonia digital repository record
- Accessed: 2026-07-20
- Jurisdiction: General commodity and market technical analysis
- Supports: Primary attribution, publication date, source scope, and the original context for RSI, true range, and directional movement systems.
- Limitations: The repository record verifies the work and provides a licensed scan, but page-level quotations are not reproduced in this package.
RMA-02 — TA-Lib classic RSI implementation
- Organization or authors: TA-Lib project; initial implementation credited to Mario Fortier
- Source type: Maintained official open-source reference implementation
- Publication or effective date: Repository history from 2000; current main branch accessed 2026-07-20
- Version: Main branch source with 1999–2025 copyright header
- URL or DOI: TA-Lib
TA_RSI.c - Accessed: 2026-07-20
- Jurisdiction: Open-source technical implementation
- Supports: An initial period average; subsequent Wilder update by multiplying prior state by
period - 1, adding the current value, and dividing byperiod; explicit compatibility differences in start alignment. - Limitations: The code smooths gains and losses inside RSI rather than exposing a standalone general-series RMA function. Its lookback and MetaStock compatibility options must not be generalized into this package's output indexing.
RMA-03 — TC2000 RSI and Wilder's RSI methodology
- Organization or authors: TC2000 Software Company
- Source type: Official platform indicator methodology
- Publication or effective date: Continuously maintained help site
- Version: Web edition accessed 2026-07-20
- URL or DOI: RSI and Wilder's RSI
- Accessed: 2026-07-20
- Jurisdiction: General market technical analysis
- Supports: Wilder smoothing as an exponential-average variant and the update
previous average + (1/n) × (current - previous average). - Limitations: The page explains smoothing in the context of RSI and uses vendor-specific function syntax.
RMA-04 — TC2000 Average True Range methodology
- Organization or authors: TC2000 Software Company
- Source type: Official platform indicator methodology
- Publication or effective date: Continuously maintained help site
- Version: Web edition accessed 2026-07-20
- URL or DOI: Average True Range
- Accessed: 2026-07-20
- Jurisdiction: General market technical analysis
- Supports: Use of Wilder smoothing for traditional ATR and equivalence to an EMA configured with span .
- Limitations: Platform formula notation and its ATR defaults are vendor conventions, not a universal standalone RMA data contract.
RMA-05 — TradingView Pine Script RMA reference
- Organization or authors: TradingView
- Source type: Official language and built-in function reference
- Publication or effective date: Pine Script version 6
- Version: Reference manual accessed 2026-07-20
- URL or DOI: Pine Script
ta.rmareference - Accessed: 2026-07-20
- Jurisdiction: Open technical documentation for a charting platform
- Supports: The maintained
RMAname,sourceandlengthinterface, and current use as a charting primitive. - Limitations: Interactive reference content is vendor-specific and may not expose every initialization detail in static page extraction.
RMA-06 — Nasdaq Trader Daily Market Files
- Organization or authors: Nasdaq, Inc.; Nasdaq Trader
- Source type: Official exchange-operated market-statistics page, field definitions, and year-to-date CSV
- Publication or effective date: 2026 year-to-date file
- Version: CSV retrieved 2026-07-22T23:23:41Z
- URL or DOI: Daily Market Files, Nasdaq data definitions, and 2026 CSV
- Accessed: 2026-07-22T23:23:41Z
- Jurisdiction: United States market statistics
- Supports: Nasdaq identifies the year-to-date file as containing selected index closing values and lists the Nasdaq Composite Index field; the CSV supplies the six observed Composite values dated 2026-03-02 through 2026-03-09.
- Limitations: RMA and EMA values are author-derived, not Nasdaq-published. The small extract is not evidence of causation, prediction, profitability, point-in-time availability, revision history, or a tradable portfolio. The package does not redistribute the full source file.
Evidence and design reconciliation
Sourced facts:
- Wilder's 1978 work is the historical source context.
- The recurrence retains of prior state and applies to the new observation.
- Classic Wilder-style implementations use an initial period average before recursive updates.
- An EMA with alpha , commonly labeled span , shares the post-seed recurrence.
- RSI and ATR are established applications of Wilder smoothing.
- Nasdaq Trader's year-to-date file contains Nasdaq Composite Index values; the six dated closes reproduced in the article are provider observations.
Explicit package choices:
- The canonical input is any homogeneous finite numeric series, not only price.
- The first RMA is the SMA of the first
periodobservations. - Pre-seed output is null and aligned one-for-one with input.
- Missing and non-finite values are rejected rather than silently skipped or imputed.
- Period one is accepted as the identity transformation.
- State uses full precision; rounding is for display only.
- Historical corrections require suffix replay from a trusted earlier state.
- The synthetic fixture is distributed under CC0-1.0.
- Historical-example RMA and EMA columns are author-derived with declared period, alpha, seed, alignment, and rounding rules.
Claims intentionally not made:
- RMA is not claimed to predict prices.
- No period is described as universally optimal.
- Equal-period RMA and EMA are not described as equivalent.
- Implementation parity does not establish trading profitability.
Full dependency-light reference implementations in both supported languages.
/** SMA-seeded Wilder moving average (RMA) for D07-F01-A04. */
export class RMAValidationError extends Error {
constructor(message: string) {
super(message);
this.name = "RMAValidationError";
}
}
export function calculateRma(
values: readonly number[],
period: number,
): Array<number | null> {
if (!Number.isSafeInteger(period) || period < 1) {
throw new RMAValidationError("period must be a positive integer.");
}
if (!Array.isArray(values)) {
throw new RMAValidationError("values must be an array.");
}
const numbers = values.map((value, index) => {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new RMAValidationError(`values[${index}] must be a finite number.`);
}
return value;
});
const alpha = 1 / period;
const output: Array<number | null> = [];
let seedSum = 0;
let rma: number | null = null;
numbers.forEach((value, zeroBasedIndex) => {
const observation = zeroBasedIndex + 1;
if (observation <= period) seedSum += value;
if (observation < period) {
output.push(null);
} else if (observation === period) {
rma = seedSum / period;
output.push(rma);
} else {
rma = (rma as number) + alpha * (value - (rma as number));
output.push(rma);
}
});
return output;
}
The embedded lab now expands to its full document height, keeping the article as the only scroll surface.