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.

Elder's Force Index (EFL) Formula Result in Google Sheets

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;
}