QQE Weighted Oscillator

Parameters: length = 14 | factor = 4.236 | smooth = 5 | weight = 2

Overview

QQE Weighted Oscillator starts from price-to-price changes, smooths those gains and losses in an RSI-like fashion, then emphasizes changes that continue moving in the same direction as the active trailing stop. The resulting oscillator stays in RSI-style space, while the companion trailing stop expands or contracts based on the recent absolute change in that oscillator.

In VectorTA the indicator returns both the weighted RSI line and the adaptive trailing stop, supports close-based candle input or raw slices, and exposes builder, streaming, and batch APIs. Non-finite values break the live stream path cleanly by clearing the previous source point before the next update resumes.

Defaults: `length = 14`, `factor = 4.236`, `smooth = 5`, and `weight = 2.0`.

Implementation Examples

Run the oscillator from a close slice or from candles with an explicit close source.

use vector_ta::indicators::qqe_weighted_oscillator::{
    qqe_weighted_oscillator,
    QqeWeightedOscillatorInput,
    QqeWeightedOscillatorParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let direct = qqe_weighted_oscillator(&QqeWeightedOscillatorInput::from_slice(
    &close,
    QqeWeightedOscillatorParams {
        length: Some(14),
        factor: Some(4.236),
        smooth: Some(5),
        weight: Some(2.0),
    },
))?;

let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let from_candles = qqe_weighted_oscillator(&QqeWeightedOscillatorInput::from_candles(
    &candles,
    "close",
    QqeWeightedOscillatorParams::default(),
))?;

println!("rsi = {:?}", direct.rsi.last());
println!("stop = {:?}", from_candles.trailing_stop.last());

API Reference

Input Methods
QqeWeightedOscillatorInput::from_candles(&Candles, "close", QqeWeightedOscillatorParams)
    -> QqeWeightedOscillatorInput

QqeWeightedOscillatorInput::from_slice(&[f64], QqeWeightedOscillatorParams)
    -> QqeWeightedOscillatorInput

QqeWeightedOscillatorInput::with_default_candles(&Candles)
    -> QqeWeightedOscillatorInput
Parameters Structure
pub struct QqeWeightedOscillatorParams {
    pub length: Option<usize>, // default 14
    pub factor: Option<f64>,   // default 4.236
    pub smooth: Option<usize>, // default 5
    pub weight: Option<f64>,   // default 2.0
}
Output Structure
pub struct QqeWeightedOscillatorOutput {
    pub rsi: Vec<f64>,
    pub trailing_stop: Vec<f64>,
}

pub struct QqeWeightedOscillatorStreamOutput {
    pub rsi: f64,
    pub trailing_stop: f64,
}
Validation & Warmup
  • The input slice must not be empty and must contain at least one finite value.
  • length and smooth must both be greater than 0.
  • factor must be finite and non-negative, and weight must be finite.
  • The direct path requires at least length + 1 finite values after the first valid source point.
  • Warmup on the vector path starts at first_valid + length, so earlier indices remain NaN.
  • Non-finite streaming input clears the previous source point and returns None.
  • Batch mode validates each sweep axis and rejects non-batch kernels.
Builder, Streaming & Batch APIs
QqeWeightedOscillatorBuilder::new()
    .length(usize)
    .factor(f64)
    .smooth(usize)
    .weight(f64)
    .kernel(Kernel)
    .apply(&Candles)
    .apply_slice(&[f64])
    .into_stream()

QqeWeightedOscillatorStream::try_new(params)
stream.update(f64) -> Option<QqeWeightedOscillatorStreamOutput>

QqeWeightedOscillatorBatchBuilder::new()
    .range(QqeWeightedOscillatorBatchRange)
    .kernel(Kernel)
    .apply_slice(&[f64])
    .apply(&Candles)

Python Bindings

Python exposes a scalar function returning a dictionary with the RSI and trailing-stop arrays, a stream class returning a two-value tuple once warmup has completed, and a batch helper that returns reshaped matrices plus the resolved parameter axes.

from vector_ta import (
    qqe_weighted_oscillator,
    qqe_weighted_oscillator_batch,
    QqeWeightedOscillatorStream,
)

single = qqe_weighted_oscillator(
    close,
    length=14,
    factor=4.236,
    smooth=5,
    weight=2.0,
)

stream = QqeWeightedOscillatorStream(length=14, factor=4.236, smooth=5, weight=2.0)
point = stream.update(close[-1])

batch = qqe_weighted_oscillator_batch(
    close,
    length_range=(14, 18, 2),
    factor_range=(3.5, 4.5, 0.5),
    smooth_range=(5, 7, 2),
    weight_range=(1.5, 2.0, 0.5),
)

print(single["rsi"][-1], single["trailing_stop"][-1])
print(batch["rows"], batch["cols"])

JavaScript/WASM Bindings

The WASM layer exposes object-returning scalar and batch helpers along with lower-level allocation and into-buffer APIs for caller-managed memory.

import init, {
  qqe_weighted_oscillator_js,
  qqe_weighted_oscillator_batch,
  qqe_weighted_oscillator_alloc,
  qqe_weighted_oscillator_free,
  qqe_weighted_oscillator_into,
} from "vector-ta-wasm";

await init();

const single = qqe_weighted_oscillator_js(close, 14, 4.236, 5, 2.0);

const batch = qqe_weighted_oscillator_batch(close, {
  length_range: [14, 18, 2],
  factor_range: [3.5, 4.5, 0.5],
  smooth_range: [5, 7, 2],
  weight_range: [1.5, 2.0, 0.5],
});

const ptrIn = qqe_weighted_oscillator_alloc(close.length);
const ptrRsi = qqe_weighted_oscillator_alloc(close.length);
const ptrStop = qqe_weighted_oscillator_alloc(close.length);
qqe_weighted_oscillator_into(ptrIn, ptrRsi, ptrStop, close.length, 14, 4.236, 5, 2.0);
qqe_weighted_oscillator_free(ptrIn, close.length);
qqe_weighted_oscillator_free(ptrRsi, close.length);
qqe_weighted_oscillator_free(ptrStop, close.length);

console.log(single.rsi, single.trailing_stop, 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