Chaikin Money Flow (CMF)
Chaikin Money Flow (CMF) is a volume-weighted average of accumulation and distribution over a specified period. It measures buying and selling pressure. A CMF above zero indicates buying pressure (accumulation), while a CMF below zero indicates selling pressure (distribution).
CMF
=CMF(data, period) Example Usage
=CMF(A2:F500, 20)
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 (e.g., 20 or 21) for the lookback window. | Required |
Returns
A two-column array of dates and their corresponding CMF values.
Source Code
Copy the following code into your Apps Script editor (Extensions > Apps Script) to use the CMF function in your spreadsheet.
cmf.js
/**
* Calculates the Chaikin Money Flow (CMF) over a specified period.
* CMF measures the amount of Money Flow Volume over a set period.
* Requires data with at least 6 columns: Date, Open, High, Low, Close, and Volume.
*
* @param {array} data - A 2D array of historical stock data (DOHLCV) from a source like GOOGLEFINANCE.
* @param {number} period - The lookback period, typically 20 or 21.
* @returns {array} A 2D array with headers: Date and CMF.
* @customfunction
*/
function CMF(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);
// --- Function-level validation for DOHLCV data ---
const columnCount = processedData[0].length;
if (columnCount < 6) {
throw new Error(`Invalid data structure for CMF. Expected at least 6 columns (Date, O, H, L, C, V), but got ${columnCount}.`);
}
// --- END of validation ---
const dataRows = processedData.slice(1);
if (period >= dataRows.length) {
throw new Error(`Invalid period. The period (${period}) cannot be greater than or equal to the number of data points (${dataRows.length}).`);
}
// Pre-calculate Money Flow Volume for each day
const moneyFlowVolumes = [];
const volumes = [];
for (let i = 0; i < dataRows.length; i++) {
const row = dataRows[i];
const high = row[2];
const low = row[3];
const close = row[4];
const volume = row[5];
volumes.push(volume);
let moneyFlowMultiplier = 0;
if (high !== low) {
moneyFlowMultiplier = ((close - low) - (high - close)) / (high - low);
}
moneyFlowVolumes.push(moneyFlowMultiplier * volume);
}
const results = [["Date", `CMF(${period})`]];
// Calculate CMF using a sliding window
let sumMoneyFlow = 0;
let sumVolume = 0;
// First, calculate for the initial period
for (let i = 0; i < period; i++) {
sumMoneyFlow += moneyFlowVolumes[i];
sumVolume += volumes[i];
}
let cmf = sumVolume === 0 ? 0 : sumMoneyFlow / sumVolume;
results.push([dataRows[period - 1][0], cmf]);
// Then, slide the window for the rest of the data
for (let i = period; i < dataRows.length; i++) {
sumMoneyFlow = sumMoneyFlow - moneyFlowVolumes[i - period] + moneyFlowVolumes[i];
sumVolume = sumVolume - volumes[i - period] + volumes[i];
cmf = sumVolume === 0 ? 0 : sumMoneyFlow / sumVolume;
results.push([dataRows[i][0], cmf]);
}
return results;
}