Geometric Bias Oscillator

Parameters: length = 100 | multiplier = 2 | atr_length = 14 | smooth = 1

Overview

Geometric Bias Oscillator evaluates whether the recent price structure is dominated by upward or downward path segments. It collects a rolling close window, orders that window, and repeatedly splits the ordered curve where an ATR-normalized point deviates far enough from the current line segment. The remaining simplified path is then decomposed into bullish and bearish legs, and those path lengths are converted into a normalized oscillator.

The final reading ranges between negative and positive values, with strong up-structure approaching 100 and strong down-structure approaching -100. ATR controls the geometric tolerance, while optional smoothing stabilizes the finished oscillator. Because the indicator depends on high, low, and close data, the streaming and batch paths both operate on HLC inputs rather than close-only slices.

Defaults: Geometric Bias Oscillator uses `length = 100`, `multiplier = 2.0`, `atr_length = 14`, and `smooth = 1`.

Implementation Examples

Compute the oscillator from HLC slices or candle data.

use vector_ta::indicators::geometric_bias_oscillator::{
    geometric_bias_oscillator,
    GeometricBiasOscillatorInput,
    GeometricBiasOscillatorParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let high = vec![101.2, 102.5, 103.0, 104.0, 104.8, 105.3];
let low = vec![99.8, 100.9, 101.5, 102.1, 103.0, 103.7];
let close = vec![100.7, 101.9, 102.6, 103.7, 104.2, 104.9];

let output = geometric_bias_oscillator(&GeometricBiasOscillatorInput::from_slices(
    &high,
    &low,
    &close,
    GeometricBiasOscillatorParams {
        length: Some(100),
        multiplier: Some(2.0),
        atr_length: Some(14),
        smooth: Some(1),
    },
))?;

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

println!("latest gbo = {:?}", output.values.last());
println!("series length = {}", candle_output.values.len());

API Reference

Input Methods
// From candles
GeometricBiasOscillatorInput::from_candles(&Candles, GeometricBiasOscillatorParams)
    -> GeometricBiasOscillatorInput

// From HLC slices
GeometricBiasOscillatorInput::from_slices(&[f64], &[f64], &[f64], GeometricBiasOscillatorParams)
    -> GeometricBiasOscillatorInput

// From candles with default parameters
GeometricBiasOscillatorInput::with_default_candles(&Candles)
    -> GeometricBiasOscillatorInput
Parameters Structure
pub struct GeometricBiasOscillatorParams {
    pub length: Option<usize>,     // default 100
    pub multiplier: Option<f64>,   // default 2.0
    pub atr_length: Option<usize>, // default 14
    pub smooth: Option<usize>,     // default 1
}
Output Structure
pub struct GeometricBiasOscillatorOutput {
    pub values: Vec<f64>,
}
Validation, Warmup & NaNs
  • Input HLC slices must be non-empty and have matching lengths.
  • length must stay within 10..=500 and cannot exceed the available data length.
  • multiplier must be finite and at least 0.1.
  • atr_length must be valid for the supplied data, and smooth must be positive.
  • The indicator requires a long enough contiguous valid run to satisfy the geometry window, ATR warmup, and smoothing warmup together.
  • NaN input gaps reset the streaming state and restart the warmup process.
  • Output is NaN-prefixed until the full warmup requirement has been satisfied.
  • Batch mode validates every axis range and rejects non-batch kernels through InvalidKernelForBatch.
Builder, Streaming & Batch APIs
// Builder
GeometricBiasOscillatorBuilder::new()
    .length(usize)
    .multiplier(f64)
    .atr_length(usize)
    .smooth(usize)
    .kernel(Kernel)
    .apply(&Candles)

GeometricBiasOscillatorBuilder::new()
    .apply_slices(&[f64], &[f64], &[f64])

GeometricBiasOscillatorBuilder::new()
    .into_stream()

// Stream
GeometricBiasOscillatorStream::try_new(GeometricBiasOscillatorParams)
GeometricBiasOscillatorStream::update(f64, f64, f64) -> Option<f64>
GeometricBiasOscillatorStream::params() -> &GeometricBiasOscillatorParams

// Batch
GeometricBiasOscillatorBatchBuilder::new()
    .kernel(Kernel)
    .length_range(start, end, step)
    .multiplier_range(start, end, step)
    .atr_length_range(start, end, step)
    .smooth_range(start, end, step)
    .apply_slices(&[f64], &[f64], &[f64])

GeometricBiasOscillatorBatchBuilder::new()
    .apply(&Candles)
Error Handling
pub enum GeometricBiasOscillatorError {
    EmptyInputData,
    AllValuesNaN,
    DataLengthMismatch { high_len: usize, low_len: usize, close_len: usize },
    InvalidLength { length: usize, min_length: usize, max_length: usize, data_len: usize },
    InvalidAtrLength { atr_length: usize, data_len: usize },
    InvalidMultiplier { multiplier: f64 },
    InvalidSmooth { smooth: usize },
    NotEnoughValidData { needed: usize, valid: usize },
    OutputLengthMismatch { expected: usize, got: usize },
    InvalidRange { axis: &'static str, start: String, end: String, step: String },
    InvalidKernelForBatch(Kernel),
}

Python Bindings

Python exposes a scalar HLC function, a streaming class, and a full batch sweep. The scalar path returns one NumPy oscillator array. Streaming returns one floating-point bias reading or `None`, while batch returns the flattened oscillator matrix together with the tested lengths, multipliers, ATR lengths, smoothing values, and output dimensions.

import numpy as np
from vector_ta import (
    geometric_bias_oscillator,
    geometric_bias_oscillator_batch,
    GeometricBiasOscillatorStream,
)

high = np.asarray(high_values, dtype=np.float64)
low = np.asarray(low_values, dtype=np.float64)
close = np.asarray(close_values, dtype=np.float64)

values = geometric_bias_oscillator(
    high,
    low,
    close,
    length=100,
    multiplier=2.0,
    atr_length=14,
    smooth=1,
    kernel="auto",
)

stream = GeometricBiasOscillatorStream(
    length=80,
    multiplier=1.5,
    atr_length=14,
    smooth=2,
)
print(stream.update(high[-1], low[-1], close[-1]))

batch = geometric_bias_oscillator_batch(
    high,
    low,
    close,
    length_range=(60, 100, 20),
    multiplier_range=(1.0, 2.0, 0.5),
    atr_length_range=(10, 14, 4),
    smooth_range=(1, 3, 2),
    kernel="auto",
)

print(batch["values"].shape)
print(batch["lengths"])
print(batch["multipliers"])

JavaScript/WASM Bindings

The WASM surface includes a direct array-returning function, a unified batch function, and raw allocation and into-buffer helpers for lower-level integration. The scalar binding returns a single oscillator array. The batch binding returns the flattened values matrix plus the tested parameter combinations and matrix dimensions.

import init, {
  geometric_bias_oscillator_js,
  geometric_bias_oscillator_batch,
} from "@vectoralpha/vector_ta";

await init();

const values = geometric_bias_oscillator_js(high, low, close, 100, 2.0, 14, 1);
console.log(values);

const batch = geometric_bias_oscillator_batch(high, low, close, {
  length_range: [60, 100, 20],
  multiplier_range: [1.0, 2.0, 0.5],
  atr_length_range: [10, 14, 4],
  smooth_range: [1, 3, 2],
});

console.log(batch.values);
console.log(batch.combos);
console.log(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