Market Facilitation Index (marketefi)

Overview

The Market Facilitation Index, developed by Bill Williams, measures the market's willingness to move price by calculating the ratio of price range (High minus Low) to trading volume, revealing how efficiently the market generates price movement per unit of volume traded. Williams designed MFI to answer the critical question of how much price responds to a given amount of trading activity, with higher values indicating that price moved significantly on relatively low volume, suggesting efficient directional movement, while lower values show that high volume produced minimal price change, indicating inefficiency or consolidation. In Williams' trading system, MFI values are typically displayed as a four-color histogram where green bars (MFI up, volume up) signal strong trend continuation as new participants enter the market, brown bars (MFI down, volume down) indicate trend exhaustion as market interest wanes, blue bars (MFI up, volume down) warn of potential manipulation or unsustainable moves on thin volume, and pink bars (MFI down, volume up) represent battles between bulls and bears that often precede breakouts. Williams emphasized that MFI should never be used in isolation but rather combined with his Fractal indicator for entry points and the Alligator indicator for trend direction, as MFI itself does not generate buy or sell signals but instead assesses whether current market conditions favor trend trading or range strategies.

Implementation Examples

Compute Market Facilitation Index from slices or candles:

use vectorta::indicators::marketefi::{marketefi, MarketefiInput, MarketefiParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with slices (high, low, volume)
let high = vec![10.0, 11.5, 12.0, 12.3];
let low = vec![9.5, 10.9, 11.8, 12.0];
let volume = vec![1500.0, 2200.0, 2000.0, 1800.0];
let input = MarketefiInput::from_slices(&high, &low, &volume, MarketefiParams::default());
let result = marketefi(&input)?;

// Using with Candles (defaults: sources "high", "low", "volume")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = MarketefiInput::with_default_candles(&candles);
let result = marketefi(&input)?;

// Access values
for v in result.values { println!("MarketEFI: {}", v); }

API Reference

Input Methods
// From slices (high, low, volume)
MarketefiInput::from_slices(&[f64], &[f64], &[f64], MarketefiParams) -> MarketefiInput

// From candles with explicit sources
MarketefiInput::from_candles(&Candles, &str, &str, &str, MarketefiParams) -> MarketefiInput

// From candles with defaults: sources "high", "low", "volume"
MarketefiInput::with_default_candles(&Candles) -> MarketefiInput
Parameters Structure
pub struct MarketefiParams; // No adjustable parameters (Default: MarketefiParams::default())
Output Structure
pub struct MarketefiOutput {
    pub values: Vec<f64>, // (high - low) / volume
}
Validation, Warmup & NaNs
  • Errors: MarketefiError::EmptyData, MismatchedDataLength, AllValuesNaN, NotEnoughValidData, ZeroOrNaNVolume.
  • Leading indices before the first bar where high, low, and volume are all finite are set to NaN.
  • For any index where volume == 0 or any input is NaN, the output at that index is NaN.
  • No warmup window; the first finite output appears at the first fully valid index.
  • Streaming: MarketefiStream::update returns None when inputs are invalid or volume == 0, else Some((high - low) / volume).
Error Handling
use vectorta::indicators::marketefi::{marketefi, MarketefiError, MarketefiInput, MarketefiParams};

let input = MarketefiInput::from_slices(&high, &low, &volume, MarketefiParams::default());
match marketefi(&input) {
    Ok(output) => process(output.values),
    Err(MarketefiError::EmptyData) => eprintln!("Empty data provided"),
    Err(MarketefiError::MismatchedDataLength) => eprintln!("Length mismatch among inputs"),
    Err(MarketefiError::AllValuesNaN) => eprintln!("All values are NaN"),
    Err(MarketefiError::NotEnoughValidData) => eprintln!("Not enough valid data"),
    Err(MarketefiError::ZeroOrNaNVolume) => eprintln!("Zero or NaN volume at a valid index"),
}

Python Bindings

Basic Usage

Calculate Market Facilitation Index using NumPy arrays:

import numpy as np
from vectorta import marketefi

# Prepare inputs
high = np.array([10.0, 11.5, 12.0, 12.3])
low = np.array([9.5, 10.9, 11.8, 12.0])
volume = np.array([1500.0, 2200.0, 2000.0, 1800.0])

# Calculate (optional: kernel="auto" | "scalar" | "avx2" | "avx512")
values = marketefi(high, low, volume)
print(values)
Streaming Real-time Updates

Compute values tick-by-tick:

from vectorta import MarketefiStream

stream = MarketefiStream()
for h, l, v in stream_source:
    val = stream.update(h, l, v)
    if val is not None:
        use(val)
Batch (single configuration)

Batch returns one row since there are no parameters to sweep:

import numpy as np
from vectorta import marketefi_batch

high = np.array([...])
low = np.array([...])
volume = np.array([...])

result = marketefi_batch(high, low, volume, kernel="auto")
values = result['values']  # shape: (1, len(data))
print(values.shape)
CUDA Acceleration

CUDA support for Market Facilitation Index is currently under development. The API will follow the same pattern as other CUDA-enabled indicators.

# Coming soon: CUDA-accelerated marketefi calculations

JavaScript/WASM Bindings

Basic Usage

Compute Market Facilitation Index with arrays of high, low, and volume:

import { marketefi_js } from 'vectorta-wasm';

const high = new Float64Array([10.0, 11.5, 12.0, 12.3]);
const low = new Float64Array([9.5, 10.9, 11.8, 12.0]);
const volume = new Float64Array([1500.0, 2200.0, 2000.0, 1800.0]);

const values = marketefi_js(high, low, volume);
console.log('MarketEFI values:', values);
Memory-Efficient Operations

Use zero-copy operations for large datasets:

import { marketefi_alloc, marketefi_free, marketefi_into, memory } from 'vectorta-wasm';

const len = high.length;
const highPtr = marketefi_alloc(len);
const lowPtr = marketefi_alloc(len);
const volumePtr = marketefi_alloc(len);
const outPtr = marketefi_alloc(len);

new Float64Array(memory.buffer, highPtr, len).set(high);
new Float64Array(memory.buffer, lowPtr, len).set(low);
new Float64Array(memory.buffer, volumePtr, len).set(volume);

// Args: high_ptr, low_ptr, volume_ptr, out_ptr, len
marketefi_into(highPtr, lowPtr, volumePtr, outPtr, len);

const values = new Float64Array(memory.buffer, outPtr, len).slice();

marketefi_free(highPtr, len);
marketefi_free(lowPtr, len);
marketefi_free(volumePtr, len);
marketefi_free(outPtr, len);
Batch Processing

Batch returns a single row (no parameter sweep):

import { marketefi_batch } from 'vectorta-wasm';

const out = marketefi_batch(high, low, volume, {});
console.log('rows:', out.rows, 'cols:', out.cols);
console.log('values length:', out.values.length);

Performance Analysis

Comparison:
View:

Across sizes, Rust CPU runs about 2.41× slower than Tulip C in this benchmark.

Loading chart...

AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05

Benchmark note

VectorTA’s Market Facilitation Index implementation includes additional edge-case handling (e.g., guarding against zero-volume inputs) compared to Tulip C. Because of this, Tulip C vs VectorTA timings may not be a strict apples-to-apples comparison.

Related Indicators