Andean Oscillator
length = 50 | signal_length = 9 Overview
The Andean Oscillator is a two-sided pressure study built from open and close rather than high and low. It recursively tracks rising and falling envelopes, converts those envelopes into bullish and bearish dispersion measures, and then smooths the stronger side into a signal line. That gives you separate bull and bear components instead of compressing both sides into one zero-centered oscillator.
In practice, the bull series expands when upward pressure dominates the evolving envelope structure, the bear series expands when downward pressure dominates, and the signal line follows whichever side is stronger at the moment. Because the implementation initializes from the first valid open/close pair and immediately begins the recursive process, the earliest finite bars are usable but still represent the least-settled part of the series.
Defaults: Andean Oscillator uses `length = 50` and `signal_length = 9`.
Implementation Examples
Compute the oscillator from open/close slices or from a candle container.
use vector_ta::indicators::andean_oscillator::{
andean_oscillator,
AndeanOscillatorInput,
AndeanOscillatorParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};
let open = vec![100.0, 101.2, 102.0, 101.4, 103.3, 104.0];
let close = vec![100.8, 102.1, 101.5, 102.7, 104.1, 103.6];
let output = andean_oscillator(&AndeanOscillatorInput::from_slices(
&open,
&close,
AndeanOscillatorParams {
length: Some(50),
signal_length: Some(9),
},
))?;
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = andean_oscillator(
&AndeanOscillatorInput::with_default_candles(&candles)
)?;
println!("Latest bull: {:?}", output.bull.last());
println!("Latest bear: {:?}", output.bear.last());
println!("Latest signal: {:?}", candle_output.signal.last()); API Reference
Input Methods ▼
// From candles
AndeanOscillatorInput::from_candles(&Candles, AndeanOscillatorParams)
-> AndeanOscillatorInput
// From open/close slices
AndeanOscillatorInput::from_slices(&[f64], &[f64], AndeanOscillatorParams)
-> AndeanOscillatorInput
// From candles with default parameters
AndeanOscillatorInput::with_default_candles(&Candles)
-> AndeanOscillatorInput Parameters Structure ▼
pub struct AndeanOscillatorParams {
pub length: Option<usize>, // default 50
pub signal_length: Option<usize>, // default 9
} Output Structure ▼
pub struct AndeanOscillatorOutput {
pub bull: Vec<f64>,
pub bear: Vec<f64>,
pub signal: Vec<f64>,
} Validation, Warmup & NaNs ▼
open.len() == close.len(); otherwise the function returnsLengthMismatch.- Empty input returns
EmptyInputData. If every open/close pair is non-finite, the function returnsAllValuesNaN. lengthandsignal_lengthmust both be greater than zero.- Leading non-finite pairs are preserved as
NaNin the output. Once the first valid pair is reached, the recursive state initializes and the first finite bull, bear, and signal values are emitted immediately. - Batch range expansion requires valid ascending integer ranges. Invalid batch kernels return
InvalidKernelForBatch.
Builder, Streaming & Batch APIs ▼
// Builder
AndeanOscillatorBuilder::new()
.length(usize)
.signal_length(usize)
.kernel(Kernel)
.apply_slices(&[f64], &[f64])
AndeanOscillatorBuilder::new()
.apply(&Candles)
AndeanOscillatorBuilder::new()
.into_stream()
// Stream
AndeanOscillatorStream::try_new(AndeanOscillatorParams)
AndeanOscillatorStream::update(open, close) -> (f64, f64, f64)
// Batch
AndeanOscillatorBatchBuilder::new()
.length_range((usize, usize, usize))
.signal_length_range((usize, usize, usize))
.kernel(Kernel)
.apply_slices(&[f64], &[f64]) Error Handling ▼
pub enum AndeanOscillatorError {
EmptyInputData,
LengthMismatch { open_len: usize, close_len: usize },
AllValuesNaN,
InvalidLength { length: usize },
InvalidSignalLength { signal_length: usize },
OutputLengthMismatch { expected: usize, got: usize },
InvalidRange { start: String, end: String, step: String },
InvalidKernelForBatch(Kernel),
} Python Bindings
Python exposes a dictionary-returning single-run function, a streaming class, and a batch function. The single-run binding returns NumPy arrays for bull, bear, and signal. Batch returns 2D matrices for each output plus the length axes and matrix shape metadata.
import numpy as np
from vector_ta import (
andean_oscillator,
andean_oscillator_batch,
AndeanOscillatorStream,
)
open_ = np.asarray(open_values, dtype=np.float64)
close = np.asarray(close_values, dtype=np.float64)
result = andean_oscillator(
open_,
close,
length=50,
signal_length=9,
kernel="auto",
)
print(result["bull"][-1], result["bear"][-1], result["signal"][-1])
batch = andean_oscillator_batch(
open_,
close,
length_range=(34, 50, 8),
signal_length_range=(7, 11, 2),
kernel="auto",
)
stream = AndeanOscillatorStream(50, 9)
print(stream.update(open_[-1], close[-1])) JavaScript/WASM Bindings
The WASM wrapper exposes object-returning single-run and batch functions plus lower-level allocation and in-place APIs. The high-level JavaScript path takes open and close typed arrays, then returns bull, bear, and signal arrays. Batch accepts two three-element ranges in its config object and returns flattened output buffers along with the parameter lists and final shape.
import init, {
andean_oscillator_js,
andean_oscillator_batch_js,
} from "/pkg/vector_ta.js";
await init();
const open = new Float64Array(openValues);
const close = new Float64Array(closeValues);
const result = andean_oscillator_js(open, close, 50, 9);
console.log(result.bull.at(-1), result.bear.at(-1), result.signal.at(-1));
const batch = andean_oscillator_batch_js(open, close, {
length_range: [34, 50, 8],
signal_length_range: [7, 11, 2],
});
console.log(batch.rows, batch.cols, batch.lengths, batch.signal_lengths); CUDA Bindings (Rust)
Additional details for the CUDA bindings can be found inside the VectorTA repository.
Performance Analysis
Across sizes, Rust CPU runs about 1.14× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU)