Money Flow Index (MFI)

Parameters: period = 14

Overview

The Money Flow Index measures buying and selling pressure by incorporating both price and volume into an oscillator that ranges from 0 to 100. MFI calculates money flow as typical price multiplied by volume for each period, then classifies this flow as positive when price rises or negative when it falls, ultimately creating a ratio that reveals whether money is flowing into or out of an asset. Values above 80 suggest overbought conditions where selling pressure may emerge, while readings below 20 indicate oversold territory where buying interest could materialize. Traders often describe MFI as a volume weighted RSI because it applies the RSI formula to money flow data rather than just price, making it particularly valuable for confirming breakouts where volume validation is crucial. What distinguishes MFI from pure momentum oscillators is its ability to detect divergences between price action and actual money flow, exposing situations where price advances on weak volume or declines despite strong accumulation.

Implementation Examples

Compute MFI from slices or candles:

use vectorta::indicators::mfi::{mfi, MfiInput, MfiParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using slices: typical price (HLC/3 recommended) and volume
let tp = vec![/* typical prices */];
let vol = vec![/* volumes */];
let params = MfiParams { period: Some(14) }; // default
let input = MfiInput::from_slices(&tp, &vol, params);
let result = mfi(&input)?;

// Using Candles with defaults: source="hlc3", period=14
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = MfiInput::with_default_candles(&candles);
let result = mfi(&input)?;

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

API Reference

Input Methods
// From slices (typical price and volume)
MfiInput::from_slices(&[f64], &[f64], MfiParams) -> MfiInput

// From candles with custom typical price source
MfiInput::from_candles(&Candles, &str, MfiParams) -> MfiInput

// From candles with defaults (source="hlc3", period=14)
MfiInput::with_default_candles(&Candles) -> MfiInput
Parameters Structure
pub struct MfiParams {
    pub period: Option<usize>, // Default: 14
}
Output Structure
pub struct MfiOutput {
    pub values: Vec<f64>, // MFI values (0..=100)
}
Validation, Warmup & NaNs
  • period > 0 and period ≤ data_len; volume length must equal typical price length.
  • First valid output at first_valid_idx + period − 1; all earlier indices are NaN.
  • If no finite pair exists: MfiError::AllValuesNaN. If insufficient valid data after first finite: MfiError::NotEnoughValidData.
  • If denominator ≈ 0 (e.g., zero volume or flat price over the window), output is 0.0 after warmup.
Error Handling
#[derive(Debug, Error)]
pub enum MfiError {
    #[error("mfi: Empty data provided.")]
    EmptyData,
    #[error("mfi: Invalid period: period = {period}, data length = {data_len}")]
    InvalidPeriod { period: usize, data_len: usize },
    #[error("mfi: Not enough valid data: needed = {needed}, valid = {valid}")]
    NotEnoughValidData { needed: usize, valid: usize },
    #[error("mfi: All values are NaN.")]
    AllValuesNaN,
}

// Example handling
match mfi(&input) {
    Ok(out) => use_values(out.values),
    Err(MfiError::EmptyData) => eprintln!("empty data"),
    Err(MfiError::InvalidPeriod { period, data_len }) => { /* fix params */ }
    Err(MfiError::NotEnoughValidData { needed, valid }) => { /* ensure enough data */ }
    Err(MfiError::AllValuesNaN) => { /* sanitize inputs */ }
}

Python Bindings

Basic Usage

Calculate MFI from NumPy arrays (defaults: period=14):

import numpy as np
from vectorta import mfi

# Typical price (HLC/3 recommended) and volume
tp = np.array([...], dtype=np.float64)
vol = np.array([...], dtype=np.float64)

# Default period
values = mfi(tp, vol, period=14)

# Specify kernel for performance
values = mfi(tp, vol, period=20, kernel="avx2")

print(values)  # NumPy array
Streaming Real-time Updates
from vectorta import MfiStream

stream = MfiStream(period=14)
for tp_i, vol_i in zip(tp_stream, vol_stream):
    val = stream.update(tp_i, vol_i)
    if val is not None:
        use(val)
Batch Parameter Optimization
import numpy as np
from vectorta import mfi_batch

tp = np.array([...], dtype=np.float64)
vol = np.array([...], dtype=np.float64)

# (start, end, step)
results = mfi_batch(tp, vol, period_range=(8, 24, 4), kernel="auto")

print(results["values"].shape)  # (num_periods, len)
print(results["periods"])       # list of tested periods
CUDA Acceleration

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

# Coming soon: CUDA-accelerated MFI calculations
# Expected patterns mirror other indicators (batch sweeps and multi-series).

JavaScript/WASM Bindings

Basic Usage

Calculate MFI in JavaScript/TypeScript:

import { mfi_js } from 'vectorta-wasm';

const tp = new Float64Array([/* typical prices (hlc3) */]);
const vol = new Float64Array([/* volumes */]);

const values = mfi_js(tp, vol, 14);
console.log('MFI values:', values);
Memory-Efficient Operations

Use zero-copy buffers for large arrays:

import { mfi_alloc, mfi_free, mfi_into, memory } from 'vectorta-wasm';

const len = tp.length;
const tpPtr = mfi_alloc(len);
const volPtr = mfi_alloc(len);
const outPtr = mfi_alloc(len);

// Copy inputs into WASM memory
new Float64Array(memory.buffer, tpPtr, len).set(tp);
new Float64Array(memory.buffer, volPtr, len).set(vol);

// Compute into pre-allocated buffer
mfi_into(tpPtr, volPtr, outPtr, len, 14);

// Read results (copy out)
const mfiVals = new Float64Array(memory.buffer, outPtr, len).slice();

mfi_free(tpPtr, len);
mfi_free(volPtr, len);
mfi_free(outPtr, len);
Batch Processing

Sweep periods using the unified JS API:

import { mfi_batch } from 'vectorta-wasm';

const cfg = { period_range: [8, 24, 4] };
const res = mfi_batch(tp, vol, cfg);

// res: { values: Float64Array, combos: Array<{ period?: number }>, rows: number, cols: number }
console.log(res.rows, res.cols);

// Extract first combo's series
const firstRow = res.values.slice(0, res.cols);

Performance Analysis

Comparison:
View:

Across sizes, Rust CPU runs about 5.58× faster than Tulip C in this benchmark.

Loading chart...

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

Related Indicators