Candle Strength Oscillator
period = 50 | atr_enabled = | atr_length = 50 | mode = bollinger Overview
Candle Strength Oscillator starts with candle anatomy rather than closing-price momentum alone. Each bar is scored by comparing the candle body to the full session range, assigning a positive score to bullish bodies and a negative score to bearish bodies. That signed value is then smoothed with a Hull-style moving-average chain so the final oscillator responds faster than a simple average while still filtering noisy bar-to-bar flips.
The smoothed strength series is paired with dynamic levels. In bollinger mode the indicator builds a moving mean and a two-standard-deviation envelope around the oscillator. In donchian mode it uses rolling highs, lows, and a midpoint channel instead. Long and short markers appear when the oscillator crosses the mid line after the levels are established. An optional ATR factor can also scale the signed candle score before smoothing, which makes the oscillator more sensitive to volatility expansion.
Defaults: Candle Strength Oscillator uses `period = 50`, `atr_enabled = false`, `atr_length = 50`, and `mode = "bollinger"`.
Implementation Examples
Compute the smoothed candle-strength regime and its level structure from OHLC slices or candle data.
use vector_ta::indicators::candle_strength_oscillator::{
candle_strength_oscillator,
CandleStrengthOscillatorInput,
CandleStrengthOscillatorParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};
let open = vec![100.0, 101.0, 102.1, 101.4, 103.0, 103.6];
let high = vec![101.4, 102.2, 103.0, 102.9, 104.4, 104.8];
let low = vec![99.5, 100.2, 101.0, 100.8, 102.2, 102.9];
let close = vec![100.9, 101.8, 101.5, 102.7, 103.8, 104.1];
let output = candle_strength_oscillator(&CandleStrengthOscillatorInput::from_slices(
&open,
&high,
&low,
&close,
CandleStrengthOscillatorParams::default(),
))?;
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = candle_strength_oscillator(
&CandleStrengthOscillatorInput::with_default_candles(&candles)
)?;
println!("strength = {:?}", output.strength.last());
println!("mid = {:?}", output.mid.last());
println!("long signal = {:?}", candle_output.long_signal.last()); API Reference
Input Methods ▼
// From candles
CandleStrengthOscillatorInput::from_candles(&Candles, CandleStrengthOscillatorParams)
-> CandleStrengthOscillatorInput
// From OHLC slices
CandleStrengthOscillatorInput::from_slices(
&[f64], &[f64], &[f64], &[f64], CandleStrengthOscillatorParams
) -> CandleStrengthOscillatorInput
// From candles with default parameters
CandleStrengthOscillatorInput::with_default_candles(&Candles)
-> CandleStrengthOscillatorInput Parameters Structure ▼
pub struct CandleStrengthOscillatorParams {
pub period: Option<usize>, // default 50
pub atr_enabled: Option<bool>, // default false
pub atr_length: Option<usize>, // default 50
pub mode: Option<String>, // default "bollinger"
} Output Structure ▼
pub struct CandleStrengthOscillatorOutput {
pub strength: Vec<f64>,
pub highs: Vec<f64>,
pub lows: Vec<f64>,
pub mid: Vec<f64>,
pub long_signal: Vec<f64>,
pub short_signal: Vec<f64>,
} Validation, Warmup & NaNs ▼
- Open, high, low, and close must all be non-empty slices with identical lengths.
- A bar is considered valid only when all four OHLC values are finite and the bar range is non-zero.
periodandatr_lengthmust both be greater than zero.modemust be eitherbollingerordonchian, with the short aliasesbbanddcalso accepted.- The indicator needs enough history to finish both the Hull-style strength warmup and the level-building warmup; otherwise it returns
NotEnoughValidData. - Strength warmup and levels warmup can be queried directly from the stream via
get_strength_warmup_period()andget_levels_warmup_period(). - If streaming encounters an invalid bar it resets its ATR, smoothing, and level state.
- Batch mode rejects invalid integer ranges and non-batch kernels.
Builder, Streaming & Batch APIs ▼
// Builder
CandleStrengthOscillatorBuilder::new()
.period(usize)
.atr_enabled(bool)
.atr_length(usize)
.mode(&'static str)
.kernel(Kernel)
.apply_slices(&[f64], &[f64], &[f64], &[f64])
CandleStrengthOscillatorBuilder::new()
.apply(&Candles)
CandleStrengthOscillatorBuilder::new()
.into_stream()
// Stream
CandleStrengthOscillatorStream::try_new(CandleStrengthOscillatorParams)
CandleStrengthOscillatorStream::update(open, high, low, close)
-> Option<CandleStrengthOscillatorPoint>
CandleStrengthOscillatorStream::reset()
CandleStrengthOscillatorStream::get_strength_warmup_period() -> usize
CandleStrengthOscillatorStream::get_levels_warmup_period() -> usize
// Batch
CandleStrengthOscillatorBatchBuilder::new()
.period_range((start, end, step))
.atr_enabled(bool)
.atr_length_range((start, end, step))
.mode(&'static str)
.kernel(Kernel)
.apply_slices(&[f64], &[f64], &[f64], &[f64]) Error Handling ▼
pub enum CandleStrengthOscillatorError {
EmptyInputData,
InputLengthMismatch { open_len: usize, high_len: usize, low_len: usize, close_len: usize },
AllValuesNaN,
InvalidPeriod { period: usize },
InvalidAtrLength { atr_length: usize },
InvalidMode { mode: String },
NotEnoughValidData { needed: usize, valid: usize },
OutputLengthMismatch { expected: usize, got: usize },
InvalidRange { field: &'static str, start: usize, end: usize, step: usize },
InvalidKernelForBatch(Kernel),
MismatchedOutputLen { dst_len: usize, expected_len: usize },
InvalidInput { msg: String },
} Python Bindings
Python exposes a tuple-returning single-run function, a streaming class, and a batch function. The single-run binding returns six NumPy arrays in order: strength, highs, lows, mid, long_signal, and short_signal. Batch returns those same channels as matrices, plus the tested periods, ATR flags, ATR lengths, modes, and the final rows and cols shape.
import numpy as np
from vector_ta import (
candle_strength_oscillator,
candle_strength_oscillator_batch,
CandleStrengthOscillatorStream,
)
open_ = np.asarray(open_values, dtype=np.float64)
high = np.asarray(high_values, dtype=np.float64)
low = np.asarray(low_values, dtype=np.float64)
close = np.asarray(close_values, dtype=np.float64)
strength, highs, lows, mid, long_signal, short_signal = candle_strength_oscillator(
open_,
high,
low,
close,
period=50,
atr_enabled=False,
atr_length=50,
mode="bollinger",
kernel="auto",
)
stream = CandleStrengthOscillatorStream(
period=34,
atr_enabled=True,
atr_length=21,
mode="donchian",
)
print(stream.update(open_[-1], high[-1], low[-1], close[-1]))
batch = candle_strength_oscillator_batch(
open_,
high,
low,
close,
period_range=(34, 50, 8),
atr_enabled=True,
atr_length_range=(14, 28, 7),
mode="bollinger",
kernel="auto",
)
print(batch["periods"], batch["atr_lengths"], batch["rows"], batch["cols"]) JavaScript/WASM Bindings
The WASM layer exposes object-returning single-run and batch wrappers plus lower-level allocation and in-place exports. The standard JavaScript wrapper returns an object with six arrays: strength, highs, lows, mid, long_signal, and short_signal. The batch wrapper returns those arrays, the tested combos, and the rows and cols shape.
import init, {
candle_strength_oscillator_js,
candle_strength_oscillator_batch_js,
} from "/pkg/vector_ta.js";
await init();
const open = new Float64Array(openValues);
const high = new Float64Array(highValues);
const low = new Float64Array(lowValues);
const close = new Float64Array(closeValues);
const result = candle_strength_oscillator_js(
open,
high,
low,
close,
50,
false,
50,
"bollinger",
);
console.log(result.strength, result.long_signal);
const batch = candle_strength_oscillator_batch_js(open, high, low, close, {
period_range: [34, 50, 8],
atr_enabled: true,
atr_length_range: [14, 28, 7],
mode: "donchian",
});
console.log(batch.combos, batch.rows, batch.cols); 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)