Monotonicity Index

Parameters: length = 20 | mode = efficiency | index_smooth = 5

Overview

Monotonicity Index evaluates each rolling window by fitting both a non-decreasing and a non-increasing monotone shape with PAVA and choosing the fit with lower mean squared error. In efficiency mode, the raw score is the fitted start-to-end move divided by the total absolute price path, scaled to 0..100. In complexity mode, the raw score is the number of fitted pool transitions relative to the maximum possible transitions, also scaled to 0..100.

The raw score is then smoothed with an SMA of length index_smooth. The indicator returns the smoothed index, a running cumulative_mean of that smoothed index, and an upper_bound equal to cumulative_mean * 2.0. Candle-based input defaults to the close source.

Defaults: length = 20, mode = "efficiency", index_smooth = 5, candle source default close.

Implementation Examples

Compute the indicator from a slice or from candle data using the default source:

use vector_ta::indicators::monotonicity_index::{
    monotonicity_index,
    MonotonicityIndexInput,
    MonotonicityIndexMode,
    MonotonicityIndexParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let data = vec![101.0, 102.0, 103.0, 102.8, 103.5, 104.2, 105.0];
let input = MonotonicityIndexInput::from_slice(
    &data,
    MonotonicityIndexParams {
        length: Some(20),
        mode: Some(MonotonicityIndexMode::Efficiency),
        index_smooth: Some(5),
    },
);
let result = monotonicity_index(&input)?;
println!("{:?}", result.index.last());

let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = MonotonicityIndexInput::with_default_candles(&candles);
let result = monotonicity_index(&input)?;
println!("{:?}", result.upper_bound.last());

API Reference

Input Methods
MonotonicityIndexInput::from_slice(&[f64], MonotonicityIndexParams)
MonotonicityIndexInput::from_candles(&Candles, source: &str, MonotonicityIndexParams)
MonotonicityIndexInput::with_default_candles(&Candles) // uses "close"
Parameters Structure
pub struct MonotonicityIndexParams {
    pub length: Option<usize>,                  // default 20
    pub mode: Option<MonotonicityIndexMode>,   // default Efficiency
    pub index_smooth: Option<usize>,           // default 5
}
  • length must be at least 2.
  • index_smooth must be at least 1.
  • mode supports efficiency and complexity.
  • Needed valid run length is length + index_smooth - 1.
Output Structure
pub struct MonotonicityIndexOutput {
    pub index: Vec<f64>,
    pub cumulative_mean: Vec<f64>,
    pub upper_bound: Vec<f64>,
}

pub struct MonotonicityIndexBatchOutput {
    pub index: Vec<f64>,
    pub cumulative_mean: Vec<f64>,
    pub upper_bound: Vec<f64>,
    pub combos: Vec<MonotonicityIndexParams>,
    pub rows: usize,
    pub cols: usize,
}
Validation, Warmup & NaNs
  • Empty input returns EmptyInputData; all-NaN input returns AllValuesNaN.
  • The indicator requires a consecutive valid run of at least length + index_smooth - 1.
  • Single-run warmup is first_valid + length + index_smooth - 2.
  • Streaming warmup period reported by get_warmup_period() is length + index_smooth - 2.
  • Non-finite streaming input resets the rolling window, smoothing state, and cumulative statistics.
  • monotonicity_index_into_slices checks all three output slice lengths.
  • Batch APIs require a batch kernel; non-batch kernels return InvalidKernelForBatch.
Error Handling
use vector_ta::indicators::monotonicity_index::{
    monotonicity_index,
    MonotonicityIndexError,
};

match monotonicity_index(&input) {
    Ok(output) => println!("Computed {} values", output.index.len()),
    Err(MonotonicityIndexError::InvalidLength { length }) =>
        eprintln!("Invalid length {}", length),
    Err(MonotonicityIndexError::InvalidIndexSmooth { index_smooth }) =>
        eprintln!("Invalid smoothing {}", index_smooth),
    Err(MonotonicityIndexError::NotEnoughValidData { needed, valid }) =>
        eprintln!("Need {} valid points, found {}", needed, valid),
    Err(e) => eprintln!("Monotonicity Index error: {}", e),
}

Python Bindings

Basic Usage

The Python function returns a 3-tuple of NumPy arrays:

import numpy as np
from vector_ta import monotonicity_index

index, cumulative_mean, upper_bound = monotonicity_index(
    np.asarray(data, dtype=np.float64),
    length=20,
    mode="efficiency",
    index_smooth=5,
    kernel="auto",
)
Streaming Real-time Updates

The Python stream class exposes both updates and the warmup period:

from vector_ta import MonotonicityIndexStream

stream = MonotonicityIndexStream(length=20, mode="efficiency", index_smooth=5)
print(stream.warmup_period)

for value in price_feed:
    point = stream.update(value)
    if point is not None:
        index, cumulative_mean, upper_bound = point
Batch Processing

Batch output returns the row-major matrices plus combo metadata:

import numpy as np
from vector_ta import monotonicity_index_batch

result = monotonicity_index_batch(
    np.asarray(data, dtype=np.float64),
    length_range=(10, 30, 10),
    index_smooth_range=(3, 7, 2),
    mode="efficiency",
    kernel="auto",
)

index = result["index"]
cumulative_mean = result["cumulative_mean"]
upper_bound = result["upper_bound"]
lengths = result["lengths"]
modes = result["modes"]
index_smooths = result["index_smooths"]
rows = result["rows"]
cols = result["cols"]

JavaScript/WASM Bindings

Basic Usage

The high-level WASM binding returns an object with the three output arrays:

import { monotonicity_index_js } from 'vectorta-wasm';

const result = monotonicity_index_js(
  new Float64Array(data),
  20,
  "efficiency",
  5
);

console.log(result.index);
console.log(result.upper_bound);
Memory-Efficient Operations

Use the low-level exports to write directly into preallocated output buffers:

import {
  monotonicity_index_alloc,
  monotonicity_index_free,
  monotonicity_index_into,
  memory,
} from 'vectorta-wasm';

const input = new Float64Array(data);
const len = input.length;

const inPtr = monotonicity_index_alloc(len);
const indexPtr = monotonicity_index_alloc(len);
const meanPtr = monotonicity_index_alloc(len);
const upperPtr = monotonicity_index_alloc(len);

new Float64Array(memory.buffer, inPtr, len).set(input);
monotonicity_index_into(inPtr, indexPtr, meanPtr, upperPtr, len, 20, "efficiency", 5);

const index = new Float64Array(memory.buffer, indexPtr, len).slice();
const cumulativeMean = new Float64Array(memory.buffer, meanPtr, len).slice();
const upperBound = new Float64Array(memory.buffer, upperPtr, len).slice();

monotonicity_index_free(inPtr, len);
monotonicity_index_free(indexPtr, len);
monotonicity_index_free(meanPtr, len);
monotonicity_index_free(upperPtr, len);
Batch Processing

Batch mode provides both object-returning and pointer-based WASM entry points:

import { monotonicity_index_batch_js } from 'vectorta-wasm';

const result = monotonicity_index_batch_js(new Float64Array(data), {
  length_range: [10, 30, 10],
  index_smooth_range: [3, 7, 2],
  mode: "efficiency",
});

console.log(result.rows, result.cols);
console.log(result.combos);
console.log(result.index);

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