Market Meanness Index

Parameters: length = 300 | source_mode = Price

Overview

Market Meanness Index counts how often a rolling source keeps moving away from the median of its current window. In Price mode the source is close. In Change mode the source is close - open. After ordering the rolling window, the implementation computes its median and counts bars where the current value is above the median and above the prior value, or below the median and below the prior value. Raw MMI is then scaled to a percentage with count * 100 / (length - 1), and mmi_smoothed is a simple moving average of mmi over the same length.

Defaults: length = 300 and source_mode = "Price". Candle input uses open and close.

Implementation Examples

Use candle helpers or explicit open/close slices:

use vector_ta::indicators::market_meanness_index::{
    market_meanness_index,
    MarketMeannessIndexInput,
    MarketMeannessIndexParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let open = vec![100.0, 100.4, 100.8, 100.6, 101.2, 101.9];
let close = vec![100.3, 100.7, 100.5, 101.0, 101.8, 102.2];

let input = MarketMeannessIndexInput::from_slices(
    &open,
    &close,
    MarketMeannessIndexParams::default(),
);
let out = market_meanness_index(&input)?;

// Candles path uses candles.open and candles.close
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_out = market_meanness_index(&MarketMeannessIndexInput::with_default_candles(&candles))?;

println!("{:?}", out.mmi.last());
println!("{:?}", out.mmi_smoothed.last());

API Reference

Input Methods
// From candles
MarketMeannessIndexInput::from_candles(
    &Candles,
    MarketMeannessIndexParams,
) -> MarketMeannessIndexInput

// From open/close slices
MarketMeannessIndexInput::from_slices(
    &[f64],
    &[f64],
    MarketMeannessIndexParams,
) -> MarketMeannessIndexInput

// Default candles params
MarketMeannessIndexInput::with_default_candles(&Candles)
    -> MarketMeannessIndexInput
Parameters and Outputs
pub struct MarketMeannessIndexParams {
    pub length: Option<usize>,        // default 300
    pub source_mode: Option<String>,  // "Price" or "Change"
}

pub struct MarketMeannessIndexOutput {
    pub mmi: Vec<f64>,
    pub mmi_smoothed: Vec<f64>,
}
Builder, Stream, and Batch Surface
MarketMeannessIndexBuilder::new()
    .length(usize)
    .source_mode(&str) -> Result<Self, MarketMeannessIndexError>
    .kernel(Kernel)
    .apply(&Candles) -> Result<MarketMeannessIndexOutput, _>

MarketMeannessIndexBuilder::new()
    .apply_slices(&[f64], &[f64]) -> Result<MarketMeannessIndexOutput, _>

MarketMeannessIndexBuilder::new()
    .into_stream() -> Result<MarketMeannessIndexStream, _>

MarketMeannessIndexStream::update(open, close) -> Option<(f64, f64)>
MarketMeannessIndexStream::update_reset_on_nan(open, close) -> Option<(f64, f64)>

MarketMeannessIndexBatchBuilder::new()
    .kernel(Kernel)
    .length_range(start, end, step)
    .length_static(length)
    .source_mode(&str) -> Result<Self, MarketMeannessIndexError>
    .apply_slices(&[f64], &[f64]) -> Result<MarketMeannessIndexBatchOutput, _>

MarketMeannessIndexBatchBuilder::new()
    .apply_candles(&Candles) -> Result<MarketMeannessIndexBatchOutput, _>
Validation, Warmup, and NaNs
  • open and close must be non-empty and equal-length.
  • length must be at least 6 and at most the data length.
  • source_mode must be "Price" or "Change" (case-insensitive on input, normalized to canonical spelling).
  • The first valid bar depends on mode: Price only needs a finite close; Change needs finite open and close.
  • The scalar validator requires at least length valid bars from the first valid bar.
  • mmi warmup prefix is first_valid + length - 1.
  • mmi_smoothed warmup prefix is first_valid + 2 * length - 2.
  • The compute path uses update_reset_on_nan, so invalid bars reset stream state and leave NaNs in the outputs for that index.
Calculation and Batch Output
  • Price mode uses close. Change mode uses close - open.
  • For each full window, the implementation orders the ring buffer into a contiguous window, computes the median, and counts directional moves that continue away from the median.
  • Raw MMI is count * 100 / (length - 1).
  • mmi_smoothed is a rolling SMA of raw MMI over the same length.
  • Batch defaults are length = (300, 300, 0) and source_mode = "Price".
  • Rust batch output is MarketMeannessIndexBatchOutput with flattened mmi and mmi_smoothed rows, plus combos, rows, and cols.
  • row_for_params() matches both length and normalized source_mode.
Error Handling
use vector_ta::indicators::market_meanness_index::MarketMeannessIndexError;

// Common errors:
// - EmptyInputData
// - DataLengthMismatch
// - AllValuesNaN
// - InvalidLength
// - InvalidSourceMode
// - NotEnoughValidData
// - OutputLengthMismatch
// - InvalidRange
// - InvalidKernelForBatch

Python Bindings

Basic Usage

The Python scalar function returns raw and smoothed MMI arrays:

from vector_ta import market_meanness_index

mmi, mmi_smoothed = market_meanness_index(
    open,
    close,
    length=300,
    source_mode="Price",
    kernel="auto",
)
Streaming Real-time Updates

The Python stream wrapper uses reset-on-invalid semantics:

from vector_ta import MarketMeannessIndexStream

stream = MarketMeannessIndexStream(length=300, source_mode="Change")

for open_i, close_i in live_bars:
    result = stream.update(open_i, close_i)
    if result is not None:
        mmi, mmi_smoothed = result
Batch Processing

Python batch returns both output matrices and parameter columns:

from vector_ta import market_meanness_index_batch

result = market_meanness_index_batch(
    open,
    close,
    length_range=(100, 300, 100),
    source_mode="Price",
    kernel="auto",
)

mmi = result["mmi"]
mmi_smoothed = result["mmi_smoothed"]
lengths = result["lengths"]
source_modes = result["source_modes"]
rows = result["rows"]
cols = result["cols"]

JavaScript/WASM Bindings

Basic Usage

The scalar JS/WASM call returns an object with raw and smoothed series:

import { market_meanness_index_js } from 'vectorta-wasm';

const result = market_meanness_index_js(open, close, 300, "Price") as {
  mmi: number[];
  mmi_smoothed: number[];
};

console.log(result.mmi);
console.log(result.mmi_smoothed);
Host Buffer API

Low-level WASM allocates one packed buffer of length len * 2, split into mmi then mmi_smoothed:

import {
  market_meanness_index_alloc,
  market_meanness_index_free,
  market_meanness_index_into_host,
} from 'vectorta-wasm';

const len = close.length;
const outPtr = market_meanness_index_alloc(len);

try {
  market_meanness_index_into_host(open, close, outPtr, 300, "Price");
} finally {
  market_meanness_index_free(outPtr, len);
}
Batch Processing

The JS batch config takes a 3-element length_range and optional source_mode:

import { market_meanness_index_batch_js } from 'vectorta-wasm';

const batch = market_meanness_index_batch_js(open, close, {
  length_range: [100, 300, 100],
  source_mode: "Price",
}) as {
  mmi: number[];
  mmi_smoothed: number[];
  rows: number;
  cols: number;
  combos: Array<{ length?: number; source_mode?: string }>;
};

console.log(batch.rows, batch.cols);
console.log(batch.combos);

CUDA Bindings (Rust)

Additional details for the CUDA bindings can be found inside the VectorTA repository.

Performance Analysis

Comparison:
View:
Placeholder data (no recorded benchmarks for this indicator)

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

Loading chart...

AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU)

Related Indicators