Awesome Oscillator
Awesome Oscillator (AO) is a momentum indicator that reflects changes in the market driving force. It is calculated by taking the difference between a 34-period and 5-period Simple Moving Average (SMA), which are based on the midpoints of the bars (Higher + Lower) / 2.
AO
=AO(data) Example Usage
=AO(A2:F500)
Parameters
| Parameter | Type | Description | Status |
|---|---|---|---|
data | Range | The input range of columns containing the Date, Open, High, Low, Close, and Volume data. The function automatically uses the High and Low values to calculate midpoints (Average Price). | Required |
Returns
A two-column array of dates and their corresponding Awesome Oscillator values.
Output Example
Source Code
Copy the following code into your Apps Script editor (Extensions > Apps Script) to use the AWESOME-OSCILLATOR function in your spreadsheet.
ao.js
/**
* Calculates the Awesome Oscillator (AO).
* AO = SMA(Median Price, 5) - SMA(Median Price, 34)
* Median Price = (High + Low) / 2
*
* @param {array} data - The input range. Must include at least 4 columns: Date, Open, High, Low.
* @returns {array} A two-column array with headers "Date" and "AO".
* @customfunction
*/
function AO(data) {
checkPremium();
// AO has fixed periods 5 and 34 usually, but we could allow params.
// Standard AO is strictly 5/34.
const fastPeriod = 5;
const slowPeriod = 34;
const processedData = getData(data);
// --- Validate Data Structure ---
const columnCount = processedData[0].length;
if (columnCount < 4) {
throw new Error(`Invalid data structure. Expected at least 4 columns (Date, O, H, L), but got ${columnCount}.`);
}
const dataRows = processedData.slice(1);
const results = [["Date", `AO (${fastPeriod}, ${slowPeriod})`]];
// We need two SMA states running on "Median Price".
// We can use buffers for calculation.
const fastBuffer = [];
const slowBuffer = [];
let fastSum = 0;
let slowSum = 0;
for (let i = 0; i < dataRows.length; i++) {
const row = dataRows[i];
const date = row[0];
const high = row[2];
const low = row[3];
const medianPrice = (high + low) / 2;
// Update Fast SMA (5)
fastBuffer.push(medianPrice);
fastSum += medianPrice;
if (fastBuffer.length > fastPeriod) {
fastSum -= fastBuffer.shift();
}
// Update Slow SMA (34)
slowBuffer.push(medianPrice);
slowSum += medianPrice;
if (slowBuffer.length > slowPeriod) {
slowSum -= slowBuffer.shift();
}
// Calculate AO
// We need both SMAs to be valid (i.e., we need at least 34 periods of data).
if (slowBuffer.length < slowPeriod) {
results.push([date, ""]);
} else {
const smaFast = fastSum / fastPeriod;
const smaSlow = slowSum / slowPeriod;
results.push([date, smaFast - smaSlow]);
}
}
// Trim Output
let firstValidIndex = -1;
for (let i = 1; i < results.length; i++) {
if (results[i][1] !== "" && results[i][1] !== null) {
firstValidIndex = i;
break;
}
}
if (firstValidIndex !== -1) {
return [results[0], ...results.slice(firstValidIndex)];
} else {
return [results[0]];
}
}