Dual Volume Divergence QQE (DVDIQQE)
period = 13 | smoothing_period = 6 | fast_multiplier = 2.618 | slow_multiplier = 4.236 | volume_type = default | center_type = dynamic Overview
The Dual Volume Divergence QQE (DVDIQQE) combines volume analysis with momentum oscillation by merging concepts from the Positive and Negative Volume Indexes with QQE style adaptive trailing levels. The indicator first constructs a Dual Volume Divergence Index (DVDI) by analyzing how price responds differently on up volume days versus down volume days. This process involves calculating separate EMAs of the Positive Volume Index and Negative Volume Index, then measuring their divergence to reveal whether accumulation or distribution dominates. The resulting oscillator then receives QQE treatment, with fast and slow trailing levels that adapt to volatility, creating dynamic bands around the main signal. The center line can operate in dynamic mode, adjusting to the cumulative average of the oscillator, or static mode with a fixed zero baseline.
DVDIQQE outputs four distinct series that work together to signal market conditions. The main DVDI oscillator reveals volume driven momentum, oscillating above and below the center line to indicate accumulation versus distribution phases. Fast and slow trailing levels create adaptive bands that expand during volatile periods and contract during quiet accumulation or distribution. When DVDI crosses these trailing levels, it signals potential trend changes with the volume conviction to sustain them. The configurable center line provides flexibility in interpretation, with dynamic mode revealing long term bias shifts and static mode offering traditional oscillator analysis. The Fibonacci based default multipliers (2.618 and 4.236) align the trailing levels with natural market rhythms.
Traders use DVDIQQE to identify volume confirmed momentum shifts and potential reversals where price and volume diverge. The indicator excels at spotting stealth accumulation or distribution phases where price appears stable but volume patterns reveal underlying institutional activity. Crosses above the fast trailing level with expanding volume suggest breakout potential, while drops below signal distribution and potential declines. The slow trailing level acts as a stronger confirmation, with crosses indicating major trend changes backed by significant volume shifts. Many traders combine DVDIQQE with price based indicators, using it as a volume filter to confirm or reject signals from traditional technical analysis.
Implementation Examples
Compute DVDIQQE from either slices or the Candles helper. Outputs include dvdi, fast_tl, slow_tl, and center_line.
use vector_ta::indicators::dvdiqqe::{dvdiqqe, DvdiqqeInput, DvdiqqeParams};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using with OHLC[V] slices
let (open, high, low, close) = (
vec![100.0, 101.0, 102.0],
vec![101.0, 102.0, 103.0],
vec![ 99.0, 100.0, 101.0],
vec![100.5, 101.5, 102.5],
);
let volume: Option<Vec<f64>> = None; // or Some(vec![...])
let params = DvdiqqeParams { period: Some(13), smoothing_period: Some(6), fast_multiplier: Some(2.618), slow_multiplier: Some(4.236), volume_type: Some("default".into()), center_type: Some("dynamic".into()), tick_size: Some(0.01) };
let input = DvdiqqeInput::from_slices(&open, &high, &low, &close, volume.as_deref(), params);
let out = dvdiqqe(&input)?;
// Using Candles with defaults
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let out = dvdiqqe(&DvdiqqeInput::with_default_candles(&candles))?;
// Access series
for (i, v) in out.dvdi.iter().enumerate() {
println!("t={i}: dvdi={}, fast_tl={}, slow_tl={}, center={}", v, out.fast_tl[i], out.slow_tl[i], out.center_line[i]);
} API Reference
Input Methods ▼
// From candles
DvdiqqeInput::from_candles(&Candles, DvdiqqeParams) -> DvdiqqeInput
// From raw OHLC[V] slices
DvdiqqeInput::from_slices(&[f64], &[f64], &[f64], &[f64], Option<&[f64]>, DvdiqqeParams) -> DvdiqqeInput
// With defaults (period=13, smoothing=6, multipliers=2.618/4.236, volume="default", center="dynamic", tick=0.01)
DvdiqqeInput::with_default_candles(&Candles) -> DvdiqqeInput Parameters Structure ▼
#[derive(Debug, Clone)]
pub struct DvdiqqeParams {
pub period: Option<usize>, // Default: 13 (must be > 0)
pub smoothing_period: Option<usize>, // Default: 6 (must be > 0)
pub fast_multiplier: Option<f64>, // Default: 2.618 (> 0)
pub slow_multiplier: Option<f64>, // Default: 4.236 (> 0)
pub volume_type: Option<String>, // Default: "default" (or "tick")
pub center_type: Option<String>, // Default: "dynamic" (or "static")
pub tick_size: Option<f64>, // Default: 0.01 (> 0)
} Output Structure ▼
pub struct DvdiqqeOutput {
pub dvdi: Vec<f64>, // Oscillator
pub fast_tl: Vec<f64>, // Fast trailing level
pub slow_tl: Vec<f64>, // Slow trailing level
pub center_line: Vec<f64> // Center (dynamic mean or 0)
} Validation, Warmup & NaNs ▼
period > 0,smoothing_period > 0,fast_multiplier > 0,slow_multiplier > 0, andtick_size > 0.- All OHLC arrays must be same length; optional
volumemust match length if provided, else fallback rules apply. - First finite close index defines the start of valid data; if none:
AllValuesNaN. - Requires at least
periodvalid points after the first finite value, elseNotEnoughValidData. - Warmup: indices before
first + (2×period − 1)areNaNfor all outputs. - Center line:
dynamicuses cumulative mean from warmup;staticuses0.0.
Error Handling ▼
use vector_ta::indicators::dvdiqqe::DvdiqqeError;
match dvdiqqe(&input) {
Ok(out) => use_outputs(out),
Err(DvdiqqeError::EmptyInputData) => eprintln!("Input data is empty"),
Err(DvdiqqeError::AllValuesNaN) => eprintln!("All values are NaN"),
Err(DvdiqqeError::MissingData) => eprintln!("Mismatched input lengths"),
Err(DvdiqqeError::InvalidPeriod { period, data_len }) =>
eprintln!("Invalid period {} for data len {}", period, data_len),
Err(DvdiqqeError::InvalidSmoothing { smoothing }) =>
eprintln!("Invalid smoothing period {}", smoothing),
Err(DvdiqqeError::InvalidTick { tick }) =>
eprintln!("Invalid tick size {}", tick),
Err(DvdiqqeError::InvalidMultiplier { multiplier, which }) =>
eprintln!("{} multiplier must be positive (got {})", which, multiplier),
Err(DvdiqqeError::NotEnoughValidData { needed, valid }) =>
eprintln!("Need {} valid points after first finite; got {}", needed, valid),
Err(DvdiqqeError::EmaError(e)) => eprintln!("EMA failed: {}", e),
} Python Bindings
Basic Usage ▼
Calculate DVDIQQE (returns 4 arrays: dvdi, fast_tl, slow_tl, center_line). All parameters are optional and default to the library’s values.
import numpy as np
from vector_ta import dvdiqqe, dvdiqqe_batch, DvdiqqeStreamPy
open_ = np.asarray([100.0, 101.0, 101.5, 102.0], dtype=np.float64)
high = np.asarray([101.0, 102.0, 102.5, 103.0], dtype=np.float64)
low = np.asarray([ 99.5, 100.5, 100.8, 101.0], dtype=np.float64)
close= np.asarray([100.5, 101.5, 101.9, 102.5], dtype=np.float64)
dvdi, fast, slow, center = dvdiqqe(
open_, high, low, close,
volume=None,
period=None,
smoothing_period=None,
fast_multiplier=None,
slow_multiplier=None,
volume_type=None,
center_type=None,
tick_size=None,
kernel=None,
)
# Streaming from Python
stream = DvdiqqeStreamPy(period=13, smoothing_period=6, fast_multiplier=2.618, slow_multiplier=4.236, volume_type="default", center_type="dynamic", tick_size=0.01)
for (o, h, l, c, v) in live_ohlcv_feed:
out = stream.update(o, h, l, c, v) # tuple(dvdi, fast_tl, slow_tl, center) or None
if out is not None:
use_realtime(*out)
Batch Sweeps ▼
# Evaluate multiple parameter sets
result = dvdiqqe_batch(
open_, high, low, close,
period_range=(10, 14, 2), # 10, 12, 14
smoothing_period_range=(4, 8, 2), # 4, 6, 8
fast_mult_range=(2.0, 3.0, 0.5), # 2.0, 2.5, 3.0
slow_mult_range=(4.0, 5.0, 0.5), # 4.0, 4.5, 5.0
kernel=None,
)
values = result["values"] # flat or matrix per build; see project docs for shape
rows = result["rows"]
cols = result["cols"]
JavaScript/WASM Bindings
Basic Usage ▼
Compute DVDIQQE in JavaScript/TypeScript. The dvdiqqe binding returns a flat block with 4 series (dvdi, fast_tl, slow_tl, center_line).
import { dvdiqqe } from 'vectorta-wasm';
const open = new Float64Array([100, 101, 102]);
const high = new Float64Array([101, 102, 103]);
const low = new Float64Array([ 99, 100, 101]);
const close = new Float64Array([100.5, 101.5, 102.5]);
// Optional arguments default to library values when omitted or null
const result = dvdiqqe(open, high, low, close, /* volume */ null, /* period */ null, /* smoothing */ null,
/* fast */ null, /* slow */ null, /* volume_type */ null, /* center_type */ null, /* tick_size */ null);
// result: { values: Float64Array, rows: 4, cols: N }
const { values, rows, cols } = result;
const plane = cols; // series are contiguous planes
const dvdi = values.slice(0 * plane, 1 * plane);
const fast_tl = values.slice(1 * plane, 2 * plane);
const slow_tl = values.slice(2 * plane, 3 * plane);
const center = values.slice(3 * plane, 4 * plane); Memory-Efficient Operations ▼
import { dvdiqqe_alloc, dvdiqqe_free, dvdiqqe_into, memory } from 'vectorta-wasm';
const len = close.length;
const oPtr = dvdiqqe_alloc(len), hPtr = dvdiqqe_alloc(len), lPtr = dvdiqqe_alloc(len), cPtr = dvdiqqe_alloc(len);
const vPtr = dvdiqqe_alloc(len), outPtr = dvdiqqe_alloc(4 * len); // 4 series × len
// Copy input data into WASM memory
new Float64Array(memory.buffer, oPtr, len).set(open);
new Float64Array(memory.buffer, hPtr, len).set(high);
new Float64Array(memory.buffer, lPtr, len).set(low);
new Float64Array(memory.buffer, cPtr, len).set(close);
// If you have volume: set it; otherwise pass 0 and let volume_type decide behavior
new Float64Array(memory.buffer, vPtr, len).fill(0);
// Compute directly into a pre-allocated output block (layout: [dvdi | fast | slow | center])
dvdiqqe_into(oPtr, hPtr, lPtr, cPtr, vPtr, len, 13, 6, 2.618, 4.236, 'default', 'dynamic', 0.01, outPtr);
const out = new Float64Array(memory.buffer, outPtr, 4 * len).slice();
const dvdi = out.slice(0 * len, 1 * len);
const fast_tl = out.slice(1 * len, 2 * len);
const slow_tl = out.slice(2 * len, 3 * len);
const center = out.slice(3 * len, 4 * len);
// Free allocations
[oPtr, hPtr, lPtr, cPtr, vPtr, outPtr].forEach(ptr => dvdiqqe_free(ptr, ptr === outPtr ? 4 * len : len)); Batch Processing ▼
Sweep multiple period/multiplier combinations in one call.
import { dvdiqqe_batch_unified } from 'vectorta-wasm';
const config = {
period_range: [10, 14, 2], // 10, 12, 14
smoothing_period_range: [4, 8, 2], // 4, 6, 8
fast_mult_range: [2.0, 3.0, 0.5], // 2.0, 2.5, 3.0
slow_mult_range: [4.0, 5.0, 0.5], // 4.0, 4.5, 5.0
};
const out = dvdiqqe_batch_unified(open, high, low, close, /* volume */ null, config, 'default', 'dynamic', 0.01);
// out: { values: Float64Array, rows: 4 * numCombos, cols, combos: [{ period, smoothing_period, fast_multiplier, slow_multiplier }, ...] }
// 4× comes from 4 output series per combo (dvdi, fast, slow, center)
CUDA
CUDA helpers are available when the Python package is built with CUDA support. Inputs must be float32; outputs are device arrays (DLPack / __cuda_array_interface__ compatible).
import numpy as np
from vector_ta import dvdiqqe_cuda_batch_dev, dvdiqqe_cuda_many_series_one_param_dev
# One series (float32)
open_f32 = np.asarray(load_open(), dtype=np.float32)
close_f32 = np.asarray(load_close(), dtype=np.float32)
dev = dvdiqqe_cuda_batch_dev(
open_f32=open_f32,
close_f32=close_f32,
volume_f32=14,
period_range=(5, 30, 5),
smoothing_period_range=(5, 30, 5),
fast_mult_range=(0.5, 2.0, 0.5),
slow_mult_range=(0.5, 2.0, 0.5),
volume_type=14,
center_type=14,
tick_size=1.0,
device_id=0,
)
# Many series (time-major)
open_tm_f32 = np.asarray(load_open_time_major_matrix(), dtype=np.float32)
rows, cols = open_tm_f32.shape
open_tm_f32 = open_tm_f32.ravel()
close_tm_f32 = np.asarray(load_close_time_major_matrix(), dtype=np.float32)
close_tm_f32 = close_tm_f32.ravel()
dev_tm = dvdiqqe_cuda_many_series_one_param_dev(
open_tm_f32=open_tm_f32,
close_tm_f32=close_tm_f32,
cols=cols,
rows=rows,
period=14,
smoothing=14,
fast_mult=1.0,
slow_mult=1.0,
volume_tm_f32=14,
volume_type=14,
center_type=14,
tick_size=1.0,
device_id=0,
) CUDA Bindings (Rust)
use vector_ta::cuda::CudaDvdiqqe;
use vector_ta::indicators::dvdiqqe::DvdiqqeBatchRange;
let cuda = CudaDvdiqqe::new(0)?;
let open: [f32] = /* ... */;
let close: [f32] = /* ... */;
let volume: Option<&[f32]> = /* ... */;
let sweep = DvdiqqeBatchRange::default();
let volume_type: str = /* ... */;
let center_type: str = /* ... */;
let tick_size: f32 = /* ... */;
let out = cuda.dvdiqqe_batch_dev(&open, &close, volume, &sweep, &volume_type, ¢er_type, tick_size)?;
let _ = out; Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-02-28