Quantitative Qualitative Estimation (QQE)
rsi_period = 14 | smoothing_factor = 5 | fast_factor = 4.236 Overview
The Quantitative Qualitative Estimation indicator enhances traditional RSI analysis by adding adaptive volatility bands and a trailing reference line, though interestingly its original creator remains unknown despite the indicator's popularity. QQE begins with a standard RSI calculation, then applies exponential smoothing to create the fast line, which represents smoothed momentum oscillating between 0 and 100. From this fast line, the indicator derives a volatility measure by computing the exponential moving average of absolute period to period changes, then scales this by the fast factor parameter to construct dynamic bands. The slow line trails the fast line within these bands, switching between upper and lower boundaries as momentum shifts direction. Traders watch for fast line crossovers of the slow line to identify momentum regime changes with built in volatility adaptation. When the fast line exceeds the slow line, bullish momentum conditions prevail; when it falls below, bearish pressure dominates. The default parameters of RSI(14), smoothing(5), and factor(4.236) balance sensitivity with stability across diverse market conditions.
Defaults: rsi_period = 14, smoothing_factor = 5, fast_factor = 4.236.
Implementation Examples
Get started with QQE in just a few lines:
use vectorta::indicators::qqe::{qqe, QqeInput, QqeParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using with a price slice
let prices = vec![100.0, 102.0, 101.0, 103.0, 104.5, 104.0];
let params = QqeParams::default(); // rsi=14, smoothing=5, fast_k=4.236
let input = QqeInput::from_slice(&prices, params);
let out = qqe(&input)?; // out.fast, out.slow
// Using with Candles (defaults: source = "close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = QqeInput::with_default_candles(&candles);
let out = qqe(&input)?;
println!("fast: {:?}", &out.fast[out.fast.len().saturating_sub(5)..]);
println!("slow: {:?}", &out.slow[out.slow.len().saturating_sub(5)..]); API Reference
Input Methods ▼
// From a price slice
QqeInput::from_slice(&[f64], QqeParams) -> QqeInput
// From candles with explicit source (e.g., "close")
QqeInput::from_candles(&Candles, &str, QqeParams) -> QqeInput
// From candles with defaults (rsi=14, smoothing=5, fast_k=4.236, source="close")
QqeInput::with_default_candles(&Candles) -> QqeInput Parameters Structure ▼
pub struct QqeParams {
pub rsi_period: Option<usize>,
pub smoothing_factor: Option<usize>,
pub fast_factor: Option<f64>,
}
// Defaults: rsi_period = 14, smoothing_factor = 5, fast_factor = 4.236 Output Structure ▼
pub struct QqeOutput {
pub fast: Vec<f64>, // EMA-smoothed RSI
pub slow: Vec<f64>, // Trailing band-following line
} Validation, Warmup & NaNs ▼
- Empty input →
QqeError::EmptyInputData; all-NaN →QqeError::AllValuesNaN. - Invalid period if
rsi_periodorsmoothing_factoris 0 or exceeds data length. - Requires enough valid data after the first finite value:
needed = rsi_period + smoothing_factor; otherwiseQqeError::NotEnoughValidData. - Warmup prefix for
slowis set toNaNuntil first + rsi_period + smoothing_factor − 2. The fast series is defined from RSI start.
Error Handling ▼
use vectorta::indicators::qqe::QqeError;
match qqe(&input) {
Ok(out) => {
process(out.fast, out.slow);
},
Err(QqeError::EmptyInputData) => eprintln!("Empty input data"),
Err(QqeError::AllValuesNaN) => eprintln!("All values are NaN"),
Err(QqeError::InvalidPeriod { period, data_len }) =>
eprintln!("Invalid period {} for length {}", period, data_len),
Err(QqeError::NotEnoughValidData { needed, valid }) =>
eprintln!("Need {} valid points, only {}", needed, valid),
Err(QqeError::DependentIndicatorError { message }) =>
eprintln!("Dependent indicator failed: {}", message),
} Python Bindings
Basic Usage ▼
import numpy as np
from vectorta import qqe
data = np.array([100, 102, 101, 103, 104.5, 104.0], dtype=np.float64)
fast, slow = qqe(data, rsi_period=14, smoothing_factor=5, fast_factor=4.236)
print('fast tail:', fast[-5:])
print('slow tail:', slow[-5:]) Streaming ▼
from vectorta import QqeStream
stream = QqeStream(14, 5, 4.236)
for price in [100, 101, 100.5, 102, 101.8]:
out = stream.update(price)
if out is not None:
fast, slow = out
print('fast:', fast, 'slow:', slow) Batch Processing ▼
import numpy as np
from vectorta import qqe_batch
data = np.random.randn(200).cumsum().astype(np.float64)
# Define parameter ranges (inclusive)
rsi_rng = (10, 20, 5) # 10,15,20
smooth_rng = (3, 7, 2) # 3,5,7
fast_k_rng = (4.236, 4.236, 0) # single value
out = qqe_batch(data, rsi_rng, smooth_rng, fast_k_rng)
rows = len(out['combos'])
cols = out['fast'].shape[1]
print('rows x cols =', rows, 'x', cols) JavaScript / WASM
Basic Usage ▼
Compute QQE and get both series in a unified buffer:
import { qqe_js } from 'vectorta-wasm';
const prices = new Float64Array([100, 102, 101, 103, 104.5, 104.0]);
const res = await qqe_js(prices, 14, 5, 4.236);
// res: { values: Float64Array, rows: 2, cols: prices.length }
const { values, rows, cols } = res as { values: Float64Array; rows: number; cols: number };
const fast = values.slice(0, cols);
const slow = values.slice(cols);
console.log('QQE fast tail:', fast.slice(-3));
console.log('QQE slow tail:', slow.slice(-3)); Memory‑Efficient Operations ▼
Use zero‑copy pointers for large arrays:
import { qqe_alloc, qqe_free, qqe_into, memory } from 'vectorta-wasm';
const data = new Float64Array([/* your prices */]);
const len = data.length;
// Allocate WASM memory for input and output (qqe_alloc reserves 2×len capacity)
const inPtr = qqe_alloc(len);
const outPtr = qqe_alloc(len); // layout: [fast..len][slow..len]
// Copy input into WASM memory
new Float64Array(memory.buffer, inPtr, len).set(data);
// Compute directly into pre‑allocated output buffer
await qqe_into(inPtr, outPtr, len, 14, 5, 4.236);
// Read results and copy out
const out = new Float64Array(memory.buffer, outPtr, len * 2);
const fast = out.slice(0, len);
const slow = out.slice(len);
// Free memory when done
qqe_free(inPtr, len);
qqe_free(outPtr, len); Batch Processing ▼
Sweep parameters and retrieve a structured result:
import { qqe_batch } from 'vectorta-wasm';
const data = new Float64Array([/* prices */]);
const config = {
rsi_period_range: [10, 20, 5],
smoothing_factor_range: [3, 7, 2],
fast_factor_range: [4.236, 4.236, 0.0]
};
const result = await qqe_batch(data, config);
// result: { fast_values: Float64Array, slow_values: Float64Array, combos, rows, cols }
console.log('rows x cols =', result.rows, 'x', result.cols);
Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05