Bollinger Bands (BB)
Function Syntax
=BOLLINGER_BANDS(data, period, mutiplier ) data(array):
Range of columns containing the date, Open, high, Low, close, volume data.period(number):
The number of periods (days) used to calculate the Simple Moving Average and standard deviation. Typically 20 periods.multiplier(number):
The multiplier applied to the standard deviation to set the width of the bands. Typically 2.
Returns:
A four-column array of dates and their corresponding SMA, Upper Band and Lower Band values.
Google Sheets Output Example
Below is an example of the resulting array when applying the custom BOLLINGER_BANDS() 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):
bb.gs
/**
* Calculates Bollinger Bands for a given dataset and periods.
*
* @param {array} data - a 2D array where the first column is the dates, and subsequent columns for open, high, low, close and volume.
* @param {number} period - The period for the SMA.
* @param {number} multiplier - The multiplier for the standard deviation (e.g., 2 for 2 standard deviations).
* @returns {array} - A 2D array with headers: Date, SMA, Upper Band, Lower Band.
* @customfunction
*/
function BOLLINGER_BANDS(data, period, multiplier) {
// Calculate the SMA (middle band)
const smaValues = SMA(data, period).slice(1); // Remove headers
// Calculate the standard deviation using STDEV_INDICATOR
const stdevValues = STDEV_INDICATOR(data, period).slice(1); // Remove headers
// Initialize Bollinger Bands array with headers
const bollingerBands = [["Date", `SMA(${period})`, `Upper Band (${multiplier})`, `Lower Band (${multiplier})`]];
// Combine SMA and standard deviation to calculate upper and lower bands
for (let i = 0; i < smaValues.length; i++) {
const date = smaValues[i][0];
const sma = smaValues[i][1];
const stdDev = stdevValues[i][1]; // Standard Deviation value
// Calculate Upper and Lower Bands
const upperBand = sma + (multiplier * stdDev);
const lowerBand = sma - (multiplier * stdDev);
// Append the results to the Bollinger Bands array
bollingerBands.push([date, sma, upperBand, lowerBand]);
}
return bollingerBands;
}
/**
*
* @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];
}))];
}
/**
* Calculates the Simple Moving Average (SMA) for a given dataset and period.
*
* @param {Array} data A range containing date and close price data from Googlefinance(), with no header row.
* @param {number} period The number of periods to calculate the SMA, e.g., 14 for a 14-day SMA.
* @returns {Array<Array>} The Simple Moving Average (SMA) of the range.
* @customfunction
*/
function SMA(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 SMA values with corresponding dates
let results = [["Date", `SMA(${period})`]]; // Include headers
// Calculate SMA for the given period
for (let i = period - 1; i < closePrices.length; i++) {
const periodPrices = closePrices.slice(i - period + 1, i + 1); // Get the slice for the period
const sma = periodPrices.reduce((acc, val) => acc + val, 0) / period; // Calculate average
// Append the current date and SMA to the results array
results.push([dates[i], sma]);
}
return results;
}
/**
* Calculates the Standard Deviation for raw price data using the SMA function.
*
* @param {array} data A range containing date and close price data from Google Finance.
* @param {number} period The period for calculating the Standard Deviation.
* @returns {array} A 2D array with headers: Date, Standard Deviation.
* @customfunction
*/
function STDEV_INDICATOR(data, period) {
// Preprocess data using getData
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.");
}
// Calculate the SMA for the given period
const smaValues = SMA(data, period).slice(1); // Remove headers, format: [Date, SMA]
// Initialize an array to hold standard deviation values
const stdevValues = [["Date", `Standard Deviation (${period})`]];
// Calculate moving standard deviation for each data point
for (let i = period - 1; i < closePrices.length; i++) {
const mean = smaValues[i - (period - 1)][1]; // Use SMA value as the mean
const periodPrices = closePrices.slice(i - period + 1, i + 1); // Get the prices for the period
// Calculate the squared differences
const squaredDifferences = periodPrices.map(val => Math.pow(val - mean, 2));
// Calculate the variance
const variance = squaredDifferences.reduce((acc, val) => acc + val, 0) / period;
// Calculate the standard deviation
const standardDeviation = Math.sqrt(variance);
// Append the results with the corresponding date
stdevValues.push([dates[i], standardDeviation]);
}
return stdevValues;
}Last updated on