Rate of Change (ROC)
Rate of Change (ROC) is a pure momentum oscillator that measures the percentage change in price from one period to the next. The ROC calculation compares the current price with the price “n” periods ago.
ROC
=ROC(data, period) Example Usage
=ROC(A2:F500, 9)
Parameters
| Parameter | Type | Description | Status |
|---|---|---|---|
data | Range | The input range of columns containing the Date, Open, High, Low, Close, and Volume data. | Required |
period | Number | The number of periods to look back (e.g., 9 or 12). | Required |
Returns
A two-column array of dates and their corresponding ROC percentage values.
Source Code
Copy the following code into your Apps Script editor (Extensions > Apps Script) to use the ROC function in your spreadsheet.
roc.js
/**
* Calculates the Rate of Change (ROC) for a specified period.
* ROC measures the percentage change in price between the current period and n periods ago.
*
* @param {array} data - The input range containing metrics. Can be a multi-column range (Date, Open, High, Low, Close, Volume) or a two-column range (Date, Value).
* @param {number} period - The number of periods to look back (e.g., 9).
* @returns {array} A two-column array with headers "Date" and "ROC".
* @customfunction
*/
function ROC(data, period) {
checkPremium();
// 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 ROC. 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 or equal to the number of data points (${values.length}).`);
}
const results = [["Date", `ROC(${period})`]];
for (let i = period; i < values.length; i++) {
const prevValue = values[i - period];
const currentValue = values[i];
if (prevValue === 0) {
results.push([dates[i], 0]); // Or handle as an error, depending on desired behavior for zero previous value
continue;
}
const roc = ((currentValue - prevValue) / prevValue) * 100;
results.push([dates[i], roc]);
}
return results;
}