Elder's Force Index (EFL)
Elder’s Force Index (EFL) determines the amount of power used to move the price of an asset. It was developed by Dr. Alexander Elder. The indicator uses price change and volume to assess the strength of the move. A high positive force index means a strong rising trend, while a low negative force index signals a strong selling pressure.
EFL
=EFL(data, period) Example Usage
=EFL(A2:F500, 13)
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 smoothing period for the EMA (typically 13). | Required |
Returns
A two-column array of dates and their corresponding EFL values.
Source Code
Copy the following code into your Apps Script editor (Extensions > Apps Script) to use the EFL function in your spreadsheet.
efl.js
/**
* Calculates the Elder's Force Index (EFL), smoothed by an Exponential Moving Average (EMA).
* This indicator uses price change and volume to measure the power behind a move.
* 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).
* @param {number} period - The lookback period for the EMA smoothing, typically 13.
* @returns {array} A 2D array with headers: Date and EFL.
* @customfunction
*/
function EFL(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 EFL. 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 - 1) {
throw new Error(`Invalid period. The period (${period}) must be smaller than the number of calculable data points (${dataRows.length - 1}).`);
}
// 1. Calculate Raw Force Index for each day
const rawForceIndex = [];
for (let i = 1; i < dataRows.length; i++) {
const date = dataRows[i][0];
const close = dataRows[i][4];
const volume = dataRows[i][5];
const prevClose = dataRows[i - 1][4];
// Alexander Elder's Force Index: (Close - PrevClose) * Volume
const force = (close - prevClose) * volume;
rawForceIndex.push([date, force]);
}
// 2. Calculate the EMA of the Raw Force Index
const results = [["Date", `EFL(${period})`]];
const multiplier = 2 / (period + 1);
const forceValues = rawForceIndex.map(item => item[1]);
const forceDates = rawForceIndex.map(item => item[0]);
// Calculate initial SMA for the first period
let sum = 0;
for (let i = 0; i < period; i++) {
sum += forceValues[i];
}
let prevEMA = sum / period;
results.push([forceDates[period - 1], prevEMA]);
// Calculate EMA for the rest
for (let i = period; i < forceValues.length; i++) {
const currentEMA = (forceValues[i] - prevEMA) * multiplier + prevEMA;
results.push([forceDates[i], currentEMA]);
prevEMA = currentEMA;
}
return results;
}