Weighted Moving Average (WMA)
Function Syntax
=WMA(data, period) data(array):
Range of columns containing the date, Open, high, Low, close, volume data.period(number):
Number of (periods) days over which to calculate the WMA.
Returns:
A two-column array of dates and their corresponding WMA values.
Google Sheets Output Example
Below is an example of the resulting array when applying the custom WMA() function in Google Sheets
Apps Scripts Code
Here’s the full Google Apps Script code.
Paste this directly into the Google Sheets Apps Script editor (Extensions → Apps Script):
wma.gs
**
* Calculates the Weighted Moving Average (WMA) for a given dataset and period.
*
* @param {array} data - A range containing date and close prices.
* @param {number} period - The number of periods for the WMA.
* @returns {array} - A 2D array with headers: Date, WMA.
* @customfunction
*/
function WMA(data, period) {
const processedData = getData(data);
const dates = processedData.slice(1).map(row => row[0]); // Extract dates
const closePrices = processedData[0].length === 2
? processedData.slice(1).map(row => row[1]) // For 2-column arrays, use the second column
: processedData.slice(1).map(row => row[4]); // Default: use index 4 for close price
// Check if the period is greater than the length of the data
if (period > closePrices.length) {
throw new Error("Error: The specified period exceeds the length of the data.");
}
// Initialize an array to store WMA values with corresponding dates
let results = [["Date", `WMA(${period})`]]; // Include headers
// Calculate weights
const weights = Array.from({ length: period }, (_, i) => period - i); // [n, n-1, ..., 1]
const totalWeight = weights.reduce((sum, weight) => sum + weight, 0); // Sum of weights
// Calculate WMA for the given period
for (let i = period - 1; i < closePrices.length; i++) {
let weightedSum = 0;
// Calculate weighted sum for the current period
for (let j = 0; j < period; j++) {
weightedSum += closePrices[i - j] * weights[j];
}
const wma = weightedSum / totalWeight; // Calculate WMA
// Append the current date and WMA to the results array
results.push([dates[i], wma]);
}
return results;
}
/**
*
* @param {Array<Array>} data - A 2D array of cell values to process. The first row should contain headers.
* @returns {Array<Array>} - A 2D array where the first row contains headers and subsequent rows contain processed data.
*
* @customfunction
*
*/
function getData(data) {
if (!data || data.length === 0) {
throw new Error("Input data is empty or invalid.");
}
// Check if the first row contains headers
const headers = Array.isArray(data[0]) && data[0].every(item => typeof item === "string")
? data[0]
: null;
if (!headers) {
// If no headers, assume the first column is the date column and process data
return data.map(row => row.map((value, index) => {
return index === 0 ? new Date(value) : parseFloat(value);
}));
}
// Process rows with headers
return [headers, ...data.slice(1).map(row => headers.map((header, index) => {
const key = header.toLowerCase();
return key === "date" ? new Date(row[index]) : parseFloat(row[index]) || row[index];
}))];
}Last updated on