Hull Butterfly Oscillator

Parameters: length = 14 | mult = 2

Overview

Hull Butterfly Oscillator uses a Hull-style weighted moving structure to measure how quickly price is bending away from its recent path. The core oscillator reacts to directional curvature, while the cumulative mean keeps a longer running reference of that motion. A third signal output then converts the relationship between the oscillator and its mean into a more directly usable state line.

In practice, that gives you three related views of the same move: the raw oscillator for short-term turns, the cumulative mean for slower context, and the signal line for regime or crossover-style logic. Candle input defaults to close, while slice input can be used directly for any precomputed source.

Defaults: Hull Butterfly Oscillator uses `length = 14`, `mult = 2.0`, and candle source `close`.

Implementation Examples

Compute the oscillator, cumulative mean, and signal line from a source series or candles.

use vector_ta::indicators::hull_butterfly_oscillator::{
    hull_butterfly_oscillator,
    HullButterflyOscillatorInput,
    HullButterflyOscillatorParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let output = hull_butterfly_oscillator(
    &HullButterflyOscillatorInput::from_slice(
        &close,
        HullButterflyOscillatorParams { length: Some(14), mult: Some(2.0) },
    ),
)?;

let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = hull_butterfly_oscillator(
    &HullButterflyOscillatorInput::with_default_candles(&candles),
)?;

println!("{:?}", output.oscillator.last());
println!("{:?}", candle_output.signal.last());

API Reference

Input Methods
// From candles
HullButterflyOscillatorInput::from_candles(&Candles, &str, HullButterflyOscillatorParams) -> HullButterflyOscillatorInput

// From a slice
HullButterflyOscillatorInput::from_slice(&[f64], HullButterflyOscillatorParams) -> HullButterflyOscillatorInput

// From candles with default parameters
HullButterflyOscillatorInput::with_default_candles(&Candles) -> HullButterflyOscillatorInput
Parameters Structure
pub struct HullButterflyOscillatorParams {
    pub length: Option<usize>,   // default 14
    pub mult: Option<f64>,       // default 2.0
}
Output Structure
pub struct HullButterflyOscillatorOutput {
    pub oscillator: Vec<f64>,
    pub cumulative_mean: Vec<f64>,
    pub signal: Vec<f64>,
}
Validation, Warmup & NaNs
  • Input must not be empty and must contain enough valid data for the requested Hull length.
  • length must be at least 2 and mult must be finite.
  • Streaming resets on non-finite values and reports its warmup through get_warmup_period().
  • Batch mode validates both parameter axes and rejects unsupported kernels through InvalidKernelForBatch.
Builder, Streaming & Batch APIs
// Builder
HullButterflyOscillatorBuilder::new()
    .length(usize)
    .mult(f64)
    .kernel(Kernel)
    .apply(&Candles, &str)

HullButterflyOscillatorBuilder::new()
    .apply_slice(&[f64])
    .into_stream()

// Stream
HullButterflyOscillatorStream::try_new(HullButterflyOscillatorParams)
HullButterflyOscillatorStream::update(f64) -> Option<(f64, f64, f64)>
HullButterflyOscillatorStream::reset()
HullButterflyOscillatorStream::get_warmup_period() -> usize

// Batch
HullButterflyOscillatorBatchBuilder::new()
    .length_range(usize, usize, usize)
    .mult_range(f64, f64, f64)
    .kernel(Kernel)
    .apply_slice(&[f64])
Error Handling
pub enum HullButterflyOscillatorError {
    EmptyInputData,
    AllValuesNaN,
    InvalidLength { length: usize, data_len: usize },
    InvalidMultiplier { mult: f64 },
    NotEnoughValidData { needed: usize, valid: usize },
    OutputLengthMismatch { expected: usize, oscillator_got: usize, cumulative_mean_got: usize, signal_got: usize },
    InvalidRange { start: String, end: String, step: String },
    InvalidKernelForBatch(Kernel),
}

Python Bindings

Python exposes a scalar function, a stream class, and a batch sweep. The scalar and stream paths return the oscillator, cumulative mean, and signal together. Batch returns reshaped matrices plus the tested lengths and multipliers.

import numpy as np
from vector_ta import (
    hull_butterfly_oscillator,
    hull_butterfly_oscillator_batch,
    HullButterflyOscillatorStream,
)

data = np.asarray(close_values, dtype=np.float64)

oscillator, cumulative_mean, signal = hull_butterfly_oscillator(
    data,
    length=14,
    mult=2.0,
    kernel="auto",
)

stream = HullButterflyOscillatorStream(length=14, mult=2.0)
print(stream.update(float(data[-1])))
print(stream.warmup_period)

batch = hull_butterfly_oscillator_batch(
    data,
    length_range=(12, 16, 2),
    mult_range=(1.5, 2.5, 0.5),
    kernel="auto",
)

print(batch["signal"].shape)
print(batch["multipliers"])

JavaScript/WASM Bindings

The WASM layer exposes scalar, batch, and into-buffer helpers. The scalar binding returns `oscillator`, `cumulative_mean`, and `signal`. The batch binding returns those flattened arrays together with parameter combos, lengths, multipliers, and matrix dimensions.

import init, {
  hull_butterfly_oscillator_js,
  hull_butterfly_oscillator_batch_js,
} from "@vectoralpha/vector_ta";

await init();

const single = hull_butterfly_oscillator_js(close, 14, 2.0);
console.log(single.oscillator);
console.log(single.signal);

const batch = hull_butterfly_oscillator_batch_js(close, {
  length_range: [12, 16, 2],
  mult_range: [1.5, 2.5, 0.5],
});

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

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