Elder's Force Index (EFI)
period = 13 Overview
Elder's Force Index (EFI), developed by Dr. Alexander Elder, combines price movement with volume to measure the power behind each market move, revealing whether bulls or bears control the action. The indicator multiplies the price change between periods by the volume traded, creating a raw force value that reflects both the direction and conviction of the move. This raw force then undergoes exponential smoothing to filter out noise and reveal the underlying pressure. By incorporating volume, EFI distinguishes between moves with genuine participation versus those lacking conviction, making it more reliable than price based oscillators alone.
EFI oscillates around zero, with positive values indicating buying pressure and negative values showing selling pressure. The distance from zero reflects the intensity of that pressure, with extreme readings suggesting potential exhaustion points. A 13 period EMA is the standard smoothing, providing a balance between responsiveness and stability. Shorter periods like 2 or 3 create a more sensitive indicator suitable for identifying precise entry and exit points. Longer periods smooth the oscillator further, making it better for confirming trend direction and filtering out false signals. Divergences between EFI and price often precede reversals, as they reveal weakening momentum despite continued price movement.
Traders use EFI in multiple ways depending on the chosen period. With short periods, zero line crossovers generate frequent trading signals, with upward crosses suggesting buy opportunities and downward crosses indicating sells. The 13 period version works well for divergence analysis and trend confirmation, helping traders stay with positions as long as EFI remains on the correct side of zero. Many traders combine EFI with moving averages, entering positions when both align. Volume spikes that push EFI to extremes often mark important tops or bottoms, especially when followed by a sharp reversal toward zero. The indicator proves particularly valuable in stocks and futures where volume data is reliable.
Implementation Examples
Compute EFI from slices or candles:
use vectorta::indicators::efi::{efi, EfiInput, EfiParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using slices (price and volume)
let price = vec![100.0, 101.0, 102.0, 101.5, 103.0];
let volume = vec![1500.0, 1200.0, 1800.0, 1600.0, 1900.0];
let params = EfiParams { period: Some(13) }; // default is 13
let input = EfiInput::from_slices(&price, &volume, params);
let out = efi(&input)?;
// Using Candles (source defaults to "close") with default parameters
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = EfiInput::with_default_candles(&candles);
let out = efi(&input)?;
// Access values
for v in out.values {
println!("EFI: {}", v);
} API Reference
Input Methods ▼
// From price and volume slices
EfiInput::from_slices(&[f64], &[f64], EfiParams) -> EfiInput
// From candles with custom source (e.g., "close")
EfiInput::from_candles(&Candles, &str, EfiParams) -> EfiInput
// From candles with defaults (source="close", period=13)
EfiInput::with_default_candles(&Candles) -> EfiInput Parameters Structure ▼
#[derive(Debug, Clone)]
pub struct EfiParams {
pub period: Option<usize>, // Default: 13
} Output Structure ▼
#[derive(Debug, Clone)]
pub struct EfiOutput {
pub values: Vec<f64>, // EFI values (EMA of (Δprice × volume))
} Validation, Warmup & NaNs ▼
- Inputs must be non-empty, same length; otherwise
EfiError::EmptyData. period > 0andperiod ≤ len; otherwiseEfiError::InvalidPeriod { period, data_len }.- If all price/volume values are
NaN:EfiError::AllValuesNaN. - Requires at least two valid points after the first finite pair; else
EfiError::NotEnoughValidData { needed: 2, valid }. - Warmup: indices before the first valid diff (where
price[i], price[i−1], volume[i]are all finite) areNaN. - Computation seeds EMA with the first valid diff; subsequent invalid triples carry the previous output forward.
- Streaming: first update returns
None(needs prior price); leadingNaNs delay seeding; laterNaNs propagate by hold‑last.
Error Handling ▼
use vectorta::indicators::efi::{efi, EfiError};
match efi(&input) {
Ok(output) => handle(output.values),
Err(EfiError::EmptyData) => eprintln!("Empty or mismatched input"),
Err(EfiError::InvalidPeriod { period, data_len }) =>
eprintln!("Invalid period {} for length {}", period, data_len),
Err(EfiError::NotEnoughValidData { needed, valid }) =>
eprintln!("Need {} valid points, have {}", needed, valid),
Err(EfiError::AllValuesNaN) => eprintln!("All values are NaN"),
} Python Bindings
Basic Usage ▼
Calculate EFI using NumPy arrays (default period = 13):
import numpy as np
from vectorta import efi
price = np.array([100.0, 101.0, 102.0, 101.5, 103.0], dtype=float)
volume = np.array([1500.0, 1200.0, 1800.0, 1600.0, 1900.0], dtype=float)
# Defaults (period=13)
values = efi(price, volume, period=13)
# Specify kernel (optional): "auto", "scalar", "avx2", "avx512"
values = efi(price, volume, period=13, kernel="auto")
print(values) Streaming Real-time Updates ▼
from vectorta import EfiStream
stream = EfiStream(period=13)
for (p, v) in feed:
value = stream.update(p, v)
if value is not None:
use(value) Batch Parameter Optimization ▼
Test multiple EMA periods efficiently:
import numpy as np
from vectorta import efi_batch
price = np.array([...], dtype=float)
volume = np.array([...], dtype=float)
results = efi_batch(price, volume, period_range=(5, 30, 5), kernel="auto")
print(results['values'].shape) # (num_periods, len(price))
print(results['periods']) # list of periods tested CUDA Acceleration ▼
CUDA support for EFI is currently under development. The API will mirror other CUDA-enabled indicators.
JavaScript/WASM Bindings
Basic Usage ▼
Calculate EFI in JavaScript/TypeScript:
import { efi_js } from 'vectorta-wasm';
const price = new Float64Array([100, 101, 102, 101.5, 103]);
const volume = new Float64Array([1500, 1200, 1800, 1600, 1900]);
const values = efi_js(price, volume, 13);
console.log('EFI values:', values); Memory-Efficient Operations ▼
Use zero-copy into/alloc/free for large datasets:
import { efi_alloc, efi_free, efi_into, memory } from 'vectorta-wasm';
const price = new Float64Array([...]);
const volume = new Float64Array([...]);
const len = price.length;
const inPricePtr = efi_alloc(len);
const inVolumePtr = efi_alloc(len);
const outPtr = efi_alloc(len);
new Float64Array(memory.buffer, inPricePtr, len).set(price);
new Float64Array(memory.buffer, inVolumePtr, len).set(volume);
// Args: in_price_ptr, in_volume_ptr, out_ptr, len, period
efi_into(inPricePtr, inVolumePtr, outPtr, len, 13);
const result = new Float64Array(memory.buffer, outPtr, len).slice();
efi_free(inPricePtr, len);
efi_free(inVolumePtr, len);
efi_free(outPtr, len);
console.log('EFI values:', result); Batch Processing ▼
Test multiple periods and get a result matrix:
import { efi_batch_js } from 'vectorta-wasm';
const price = new Float64Array([...]);
const volume = new Float64Array([...]);
// Config uses a tuple for period range: (start, end, step)
const output = efi_batch_js(price, volume, { period_range: [5, 30, 5] });
// output = { values: Float64Array(flat), combos: [{ period }, ...], rows, cols }
const { values, combos, rows, cols } = output;
// Reshape flat values into [rows x cols]
const matrix = [];
for (let r = 0; r < rows; r++) {
const start = r * cols;
matrix.push(values.slice(start, start + cols));
}
console.log('Periods tested:', combos.map(c => c.period)); Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05
Related Indicators
Accumulation/Distribution
Technical analysis indicator
Accumulation/Distribution Oscillator
Technical analysis indicator
Balance of Power
Technical analysis indicator
Chaikin Flow Oscillator
Technical analysis indicator
Ease of Movement
Technical analysis indicator
Klinger Volume Oscillator
Technical analysis indicator