Kurtosis (Excess Kurtosis)

Parameters: period = 5

Overview

Kurtosis measures the presence of extreme price movements and outliers by quantifying how peaked or flat the distribution of returns appears compared to a normal distribution, revealing when markets shift between calm and crisis modes. Positive excess kurtosis indicates fat tails with more extreme moves than expected from normal distributions, signaling increased risk of sudden price spikes or crashes that standard deviation alone cannot capture. When kurtosis rises sharply, it warns traders that the market has entered a regime where extreme events become more likely, requiring wider stops and reduced position sizes to account for tail risk. Negative kurtosis reveals platykurtic distributions where prices cluster tightly around the mean with fewer outliers, indicating stable conditions favorable for mean reversion strategies and tighter risk management. Options traders particularly monitor kurtosis because it directly impacts pricing models, as high kurtosis environments require adjustments to implied volatility smiles to accurately price tail risk protection. The indicator provides crucial intelligence about market regime changes, as shifts from low to high kurtosis often precede volatility expansions, while transitions from high to low kurtosis suggest markets returning to equilibrium after crisis periods.

Implementation Examples

Compute excess kurtosis from slices or candles:

use vector_ta::indicators::kurtosis::{kurtosis, KurtosisInput, KurtosisParams};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

// From a numeric slice
let prices = vec![100.0, 101.0, 102.0, 99.0, 104.0, 98.0];
let params = KurtosisParams { period: Some(5) }; // Default is 5
let input = KurtosisInput::from_slice(&prices, params);
let out = kurtosis(&input)?;

// From Candles with default source and params (source = "hl2", period=5)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = KurtosisInput::with_default_candles(&candles);
let out = kurtosis(&input)?;

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

API Reference

Input Methods
// From price slice
KurtosisInput::from_slice(&[f64], KurtosisParams) -> KurtosisInput

// From candles with custom source
KurtosisInput::from_candles(&Candles, &str, KurtosisParams) -> KurtosisInput

// From candles with default params (source="hl2", period=5)
KurtosisInput::with_default_candles(&Candles) -> KurtosisInput
Parameters Structure
#[derive(Debug, Clone)]
pub struct KurtosisParams {
    pub period: Option<usize>, // Default: 5
}
Output Structure
#[derive(Debug, Clone)]
pub struct KurtosisOutput {
    pub values: Vec<f64>, // Excess kurtosis values (m4/m2^2 - 3)
}
Validation, Warmup & NaNs
  • period > 0 and period ≤ data.len(); otherwise KurtosisError::InvalidPeriod.
  • Requires at least period valid points after the first finite value; otherwise KurtosisError::NotEnoughValidData.
  • Indices before first_valid + period − 1 are NaN (warmup prefix).
  • If any input inside a window is NaN, that window’s output is NaN.
  • If the second central moment m2 is ~0 (no variance), the output is NaN.
  • Streaming: returns None until the first period values are buffered; then emits one value per update.
Error Handling
use vector_ta::indicators::kurtosis::{kurtosis, KurtosisError, KurtosisInput, KurtosisParams};

let input = KurtosisInput::from_slice(&prices, KurtosisParams { period: Some(5) });
match kurtosis(&input) {
    Ok(output) => process(output.values),
    Err(KurtosisError::EmptyInputData) => eprintln!("Empty input slice"),
    Err(KurtosisError::AllValuesNaN) => eprintln!("All input values are NaN"),
    Err(KurtosisError::InvalidPeriod { period, data_len }) =>
        eprintln!("Invalid period {} for length {}", period, data_len),
    Err(KurtosisError::NotEnoughValidData { needed, valid }) =>
        eprintln!("Need {} valid points, only {} available", needed, valid),
    Err(KurtosisError::ZeroOrMissingPeriod) =>
        eprintln!("Zero or missing period"),
}

Python Bindings

Basic Usage

Calculate excess kurtosis using NumPy arrays:

import numpy as np
from vector_ta import kurtosis

prices = np.array([100.0, 101.0, 102.0, 99.0, 104.0, 98.0], dtype=np.float64)

# period required; optional kernel: "auto", "scalar", "avx2", "avx512" (if available)
values = kurtosis(prices, period=5, kernel="auto")
print(values)
Streaming
from vector_ta import KurtosisStream

stream = KurtosisStream(period=5)
for price in price_feed:
    value = stream.update(price)
    if value is not None:
        print("kurtosis:", value)
Batch Parameter Optimization

Evaluate a sweep of period values:

import numpy as np
from vector_ta import kurtosis_batch

prices = np.array([...], dtype=np.float64)

# (start, end, step)
res = kurtosis_batch(prices, period_range=(5, 20, 5), kernel="auto")

# Result dict contains a 2D array and tested periods
values_2d = res["values"]     # shape: [rows, len(prices)]
periods = res["periods"]      # tested period values
print(periods, values_2d.shape)
CUDA Acceleration

CUDA helpers are available when the Python package is built with CUDA support. Inputs must be float32; outputs are device arrays (DLPack / __cuda_array_interface__ compatible).

import numpy as np
from vector_ta import kurtosis_cuda_batch_dev, kurtosis_cuda_many_series_one_param_dev

# One series (float32)
data_f32 = np.asarray(load_data(), dtype=np.float32)

dev = kurtosis_cuda_batch_dev(
    data_f32=data_f32,
    period_range=(5, 30, 5),
    device_id=0,
)

# Many series (time-major)
data_tm_f32 = np.asarray(load_data_time_major_matrix(), dtype=np.float32)

dev_tm = kurtosis_cuda_many_series_one_param_dev(
    data_tm_f32=data_tm_f32,
    period=14,
    device_id=0,
)

JavaScript/WASM Bindings

Basic Usage

Calculate Kurtosis in JavaScript/TypeScript:

import { kurtosis_js } from 'vectorta-wasm';

const prices = new Float64Array([100.0, 101.0, 102.0, 99.0, 104.0, 98.0]);
const values = kurtosis_js(prices, 5);
console.log('kurtosis:', values);
Memory-Efficient Operations

Use zero-copy operations for large datasets:

import { kurtosis_alloc, kurtosis_free, kurtosis_into, memory } from 'vectorta-wasm';

const prices = new Float64Array([/* your data */]);
const n = prices.length;

const inPtr = kurtosis_alloc(n);
const outPtr = kurtosis_alloc(n);
new Float64Array(memory.buffer, inPtr, n).set(prices);

// Args: in_ptr, out_ptr, len, period
kurtosis_into(inPtr, outPtr, n, 5);

const out = new Float64Array(memory.buffer, outPtr, n).slice();
kurtosis_free(inPtr, n);
kurtosis_free(outPtr, n);
console.log('kurtosis:', out);
Batch Processing

Unified batch API returns values and combos:

import { kurtosis_batch } from 'vectorta-wasm';

const prices = new Float64Array([/* historical prices */]);
const cfg = { period_range: [5, 20, 5] as [number, number, number] };
const result = kurtosis_batch(prices, cfg);

// result: { values: Float64Array, combos: { period: number | null }[], periods: number[], rows: number, cols: number }
const { values, combos, periods, rows, cols } = result;
console.log('rows x cols =', rows, 'x', cols);
console.log('periods tested:', periods);

CUDA Bindings (Rust)

use vector_ta::cuda::CudaKurtosis;
use vector_ta::indicators::kurtosis::KurtosisBatchRange;

let cuda = CudaKurtosis::new(0)?;

let data_f32: [f32] = /* ... */;
let sweep = KurtosisBatchRange::default();

let out = cuda.kurtosis_batch_dev(&data_f32, &sweep)?;
let _ = out;

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators