Adaptive Bandpass Trigger Oscillator

Parameters: delta = 0.1 | alpha = 0.07

Overview

Adaptive Bandpass Trigger Oscillator is a cycle-focused study that tries to isolate a tradable oscillation from a raw price stream. The indicator first smooths the input, estimates the dominant cycle adaptively, then feeds that estimate into an adaptive band-pass stage. The primary output is the in-phase component, while the lead line acts as a trigger that is phase-advanced relative to the core cycle readout.

In practice, the in-phase line is the cleaner cycle track and the lead line is the faster companion used for crossings and early turns. Because the implementation needs a minimum amount of valid history before the cycle estimate stabilizes, the first valid bars are warmup rather than actionable output. This is especially true for the lead series, which begins one bar later than the in-phase series.

Defaults: Adaptive Bandpass Trigger Oscillator uses `delta = 0.1` and `alpha = 0.07`.

Implementation Examples

Compute the oscillator from a slice or from a candle source field.

use vector_ta::indicators::adaptive_bandpass_trigger_oscillator::{
    adaptive_bandpass_trigger_oscillator,
    AdaptiveBandpassTriggerOscillatorInput,
    AdaptiveBandpassTriggerOscillatorParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let close = vec![100.0, 100.8, 101.2, 100.9, 101.7, 102.1, 101.6, 102.4];

let output = adaptive_bandpass_trigger_oscillator(
    &AdaptiveBandpassTriggerOscillatorInput::from_slice(
        &close,
        AdaptiveBandpassTriggerOscillatorParams {
            delta: Some(0.1),
            alpha: Some(0.07),
        },
    )
)?;

let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = adaptive_bandpass_trigger_oscillator(
    &AdaptiveBandpassTriggerOscillatorInput::with_default_candles(&candles)
)?;

println!("Latest in-phase: {:?}", output.in_phase.last());
println!("Latest lead: {:?}", candle_output.lead.last());

API Reference

Input Methods
// From candles and a named source field
AdaptiveBandpassTriggerOscillatorInput::from_candles(
    &Candles,
    &str,
    AdaptiveBandpassTriggerOscillatorParams,
) -> AdaptiveBandpassTriggerOscillatorInput

// From a slice
AdaptiveBandpassTriggerOscillatorInput::from_slice(
    &[f64],
    AdaptiveBandpassTriggerOscillatorParams,
) -> AdaptiveBandpassTriggerOscillatorInput

// From candles with default parameters
AdaptiveBandpassTriggerOscillatorInput::with_default_candles(&Candles)
    -> AdaptiveBandpassTriggerOscillatorInput
Parameters Structure
pub struct AdaptiveBandpassTriggerOscillatorParams {
    pub delta: Option<f64>, // default 0.1
    pub alpha: Option<f64>, // default 0.07
}
Output Structure
pub struct AdaptiveBandpassTriggerOscillatorOutput {
    pub in_phase: Vec<f64>,
    pub lead: Vec<f64>,
}
Validation, Warmup & NaNs
  • The input slice must be non-empty and contain at least one finite value.
  • delta and alpha must both be finite numbers strictly between 0 and 1.
  • The algorithm requires at least 12 valid samples. If fewer valid values are present, it returns NotEnoughValidData.
  • The in-phase series warms up after 11 valid samples. The lead series starts one bar later.
  • Streaming resets on a non-finite input value.
  • Batch range expansion requires valid ascending floating-point ranges, and non-batch kernels are rejected for the batch entry point.
Builder, Streaming & Batch APIs
// Builder
AdaptiveBandpassTriggerOscillatorBuilder::new()
    .delta(f64)
    .alpha(f64)
    .kernel(Kernel)
    .apply_slice(&[f64])

AdaptiveBandpassTriggerOscillatorBuilder::new()
    .apply(&Candles)

AdaptiveBandpassTriggerOscillatorBuilder::new()
    .into_stream()

// Stream
AdaptiveBandpassTriggerOscillatorStream::try_new(AdaptiveBandpassTriggerOscillatorParams)
AdaptiveBandpassTriggerOscillatorStream::update(f64) -> Option<(f64, f64)>
AdaptiveBandpassTriggerOscillatorStream::get_warmup_period() -> usize

// Batch
AdaptiveBandpassTriggerOscillatorBatchBuilder::new()
    .delta(start, end, step)
    .alpha(start, end, step)
    .kernel(Kernel)
    .apply_slice(&[f64])
Error Handling
pub enum AdaptiveBandpassTriggerOscillatorError {
    EmptyInputData,
    AllValuesNaN,
    InvalidDelta { delta: f64 },
    InvalidAlpha { alpha: f64 },
    NotEnoughValidData { needed: usize, valid: usize },
    OutputLengthMismatch {
        expected: usize,
        in_phase_got: usize,
        lead_got: usize,
    },
    InvalidRange { start: String, end: String, step: String },
    InvalidKernelForBatch(Kernel),
}

Python Bindings

Python exposes a tuple-returning single-run function, a streaming class, and a batch function. The single-run binding returns two NumPy arrays: in_phase and lead. Batch returns a dictionary containing both matrices plus the tested delta and alpha axes.

import numpy as np
from vector_ta import (
    adaptive_bandpass_trigger_oscillator,
    adaptive_bandpass_trigger_oscillator_batch,
    AdaptiveBandpassTriggerOscillatorStream,
)

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

in_phase, lead = adaptive_bandpass_trigger_oscillator(
    data,
    delta=0.1,
    alpha=0.07,
    kernel="auto",
)

batch = adaptive_bandpass_trigger_oscillator_batch(
    data,
    delta_range=(0.08, 0.16, 0.04),
    alpha_range=(0.05, 0.09, 0.02),
    kernel="auto",
)

stream = AdaptiveBandpassTriggerOscillatorStream(0.1, 0.07)
print(stream.warmup_period)
print(stream.update(data[-1]))

JavaScript/WASM Bindings

The WASM layer exposes object-returning single-run and batch wrappers plus lower-level allocation and in-place exports. The normal JavaScript path returns an object with in_phase and lead typed arrays. The batch wrapper adds the tested delta and alpha lists plus the final matrix shape.

import init, {
  adaptive_bandpass_trigger_oscillator_js,
  adaptive_bandpass_trigger_oscillator_batch_js,
} from "/pkg/vector_ta.js";

await init();

const data = new Float64Array(closeValues);

const result = adaptive_bandpass_trigger_oscillator_js(data, 0.1, 0.07);
console.log(result.in_phase, result.lead);

const batch = adaptive_bandpass_trigger_oscillator_batch_js(data, {
  delta_range: [0.08, 0.16, 0.04],
  alpha_range: [0.05, 0.09, 0.02],
});

console.log(batch.deltas, batch.alphas, batch.rows, batch.cols);

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