Relative Moving Average (RMA)


The Relative Moving Average (RMA) is a type of smoothed moving average introduced by J. Welles Wilder Jr., serving as a core component in his famous indicators like the Relative Strength Index (RSI) and Average True Range (ATR). Similar to an Exponential Moving Average (EMA), the RMA’s primary function is to smooth price data to clarify the direction of the underlying trend. Its unique characteristic is its specific smoothing formula, which provides a consistent filtering effect that helps reduce market noise without the significant lag of a Simple Moving Average (SMA).

RMA

=RMA(data, period)

Example Usage

=RMA(A2:F500, 20)

Parameters

Parameter Type Description Status
data
Range
Range of columns containing the date, Open, high, Low, close, volume data.
Required
period
Number
Number of periods (days) over which the RMA is calculated.
Required

Returns

A two-column array of dates and their corresponding RMA values.

Output Example

RMA Formula Result in Google Sheets

Source Code

Copy the following code into your Apps Script editor (Extensions > Apps Script) to use the RMA function in your spreadsheet.

rma.js
/**
 * Calculates the Relative Moving Average (RMA) for a given dataset and period.
 * Functions like SMA and EMA, taking raw data and returning date-aligned results.
 *
 * @param {array} data - An array of historical stock data. Can be a single column of prices or a multi-column array from GOOGLEFINANCE.
 * @param {number} period The number of periods to calculate the RMA.
 * @returns {Array<Array>} A 2D array with headers: Date and RMA.
 * @customfunction
 */
function RMA(data, period) {
  // Argument validation
  if (arguments.length !== 2) {
    throw new Error(`Wrong number of arguments. Expected 2, but got ${arguments.length}.`);
  }
  if (typeof period !== 'number' || period <= 0 || !Number.isInteger(period)) {
    throw new Error(`Invalid period. The period must be a positive integer. Got: ${period}`);
  }

  const processedData = getData(data);

  // --- NEW: Function-level validation ---
  const columnCount = processedData[0].length;
  if (columnCount > 2 && columnCount < 5) {
      throw new Error(`Invalid data structure for RMA. For multi-column data, expected at least 5 columns (Date, O, H, L, C), but got ${columnCount}. For simple data, expected 2 columns (Date, Value).`);
  }
  // --- END of validation ---

  const dates = processedData.slice(1).map(row => row[0]);
  const values = getValues(processedData);

  if (period > values.length) {
    throw new Error(`Invalid period. The period (${period}) cannot be greater than the number of data points (${values.length}).`);
  }

  let results = [["Date", `RMA(${period})`]];
  const multiplier = 1 / period;

  // Calculate SMA for the first 'period' data points as the initial RMA
  let sum = 0;
  for (let i = 0; i < period; i++) {
    sum += values[i];
  }
  let initialRMA = sum / period;
  results.push([dates[period - 1], initialRMA]);

  // Calculate RMA for the remaining data points
  for (let i = period; i < values.length; i++) {
    const currentChange = values[i];
    const prevRMA = results[results.length - 1][1];
    const currentRMA = (currentChange - prevRMA) * multiplier + prevRMA;
    results.push([dates[i], currentRMA]);
  }

  return results;
}