Qstick

Parameters: period = 5

Overview

Qstick measures the average size and direction of candlestick bodies over a rolling window by calculating the simple moving average of the close minus open difference for each bar, revealing whether buyers or sellers have controlled the session closes. Positive Qstick values indicate that closing prices have consistently exceeded opening prices, suggesting bullish pressure as buyers pushed price higher throughout the measured periods. Negative values show the opposite, with sellers dominating to close prices below opens and signaling bearish control. The magnitude of Qstick reflects the strength of directional bias, with larger absolute values indicating more decisive control by one side and readings near zero suggesting balanced or choppy conditions where neither buyers nor sellers establish dominance. Traders interpret Qstick crossovers above zero as bullish signals showing buyers taking control, while drops below zero warn of seller dominance and potential downtrends. Divergences between Qstick and price can reveal weakening momentum, such as when price makes new highs but Qstick fails to confirm by showing smaller positive readings, indicating that although price rose the intrabar strength has diminished. The default five period setting captures short term shifts in body direction for tactical entries and exits, though longer periods smooth the signal for strategic trend assessment at the cost of responsiveness.

Defaults: period=5.

Implementation Examples

Compute Qstick from slices or candles:

use vectorta::indicators::qstick::{qstick, QstickInput, QstickParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using slices: open and close
let open = vec![/* ... */];
let close = vec![/* ... */];
let params = QstickParams { period: Some(5) }; // default
let input = QstickInput::from_slices(&open, &close, params);
let result = qstick(&input)?;

// Using Candles with defaults: sources ("open", "close"), period=5
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = QstickInput::with_default_candles(&candles);
let result = qstick(&input)?;

// Access values
for v in result.values { println!("Qstick: {}", v); }

API Reference

Input Methods
// From open/close slices
QstickInput::from_slices(&[f64], &[f64], QstickParams) -> QstickInput

// From candles with custom sources
QstickInput::from_candles(&Candles, &str, &str, QstickParams) -> QstickInput

// From candles with defaults (sources: "open", "close"; period=5)
QstickInput::with_default_candles(&Candles) -> QstickInput
Parameters Structure
pub struct QstickParams {
    pub period: Option<usize>, // Default: 5
}
Output Structure
pub struct QstickOutput {
    pub values: Vec<f64>, // Average (Close - Open)
}
Validation, Warmup & NaNs
  • period > 0 and period ≤ min(len(open), len(close)); otherwise QstickError::InvalidPeriod.
  • First valid index is the first bar where both Open and Close are finite; if none, QstickError::AllValuesNaN.
  • There must be at least period valid bars from that index; else QstickError::NotEnoughValidData.
  • Outputs before first_valid + period − 1 are NaN; streaming returns None until filled.
Error Handling
use vectorta::indicators::qstick::{qstick, QstickError};

match qstick(&input) {
    Ok(output) => process(output.values),
    Err(QstickError::AllValuesNaN) => eprintln!("No finite Open/Close pairs found"),
    Err(QstickError::InvalidPeriod { period, data_len }) => {
        eprintln!("Invalid period: {} (data_len={})", period, data_len)
    }
    Err(QstickError::NotEnoughValidData { needed, valid }) => {
        eprintln!("Need {} data points, only {} valid", needed, valid)
    }
}

Python Bindings

Basic Usage

Calculate Qstick using NumPy arrays (default period=5):

import numpy as np
from vectorta import qstick

# Prepare open/close arrays
open_arr = np.array([...], dtype=np.float64)
close_arr = np.array([...], dtype=np.float64)

# Calculate with defaults
values = qstick(open_arr, close_arr, period=5)

# Or specify kernel ("auto", "avx2", "avx512", "scalar")
values = qstick(open_arr, close_arr, period=10, kernel="auto")

print(values)
Streaming Real-time Updates

Process pairs of Open/Close values per bar:

from vectorta import QstickStream

stream = QstickStream(period=5)
for o, c in zip(live_open, live_close):
    q = stream.update(float(o), float(c))
    if q is not None:
        handle(q)
Batch Parameter Optimization

Test multiple periods efficiently:

import numpy as np
from vectorta import qstick_batch

open_arr = np.array([...], dtype=np.float64)
close_arr = np.array([...], dtype=np.float64)

res = qstick_batch(open_arr, close_arr, period_range=(5, 20, 5), kernel="auto")

# res: { 'values': ndarray[rows, cols], 'periods': ndarray[rows] }
print(res['values'].shape, res['periods'])

CUDA Acceleration

Status

CUDA support for Qstick is coming soon. The API will follow the same pattern as other CUDA-enabled indicators.

JavaScript/WASM Bindings

Basic Usage

Calculate Qstick in JavaScript/TypeScript:

import { qstick_js } from 'vectorta-wasm';

const open = new Float64Array([/* ... */]);
const close = new Float64Array([/* ... */]);

const values = qstick_js(open, close, 5);
console.log('Qstick values:', values);
Memory-Efficient Operations

Use zero-copy buffers for large arrays:

import { qstick_alloc, qstick_free, qstick_into, memory } from 'vectorta-wasm';

const len = open.length;
const openPtr = qstick_alloc(len);
const closePtr = qstick_alloc(len);
const outPtr = qstick_alloc(len);

// Copy inputs into WASM memory
new Float64Array(memory.buffer, openPtr, len).set(open);
new Float64Array(memory.buffer, closePtr, len).set(close);

// Compute into pre-allocated buffer
qstick_into(openPtr, closePtr, outPtr, len, 5);

// Read results (copy out)
const qvals = new Float64Array(memory.buffer, outPtr, len).slice();

qstick_free(openPtr, len);
qstick_free(closePtr, len);
qstick_free(outPtr, len);
Batch Processing

Sweep periods using the unified JS API:

import { qstick_batch } from 'vectorta-wasm';

const cfg = { period_range: [5, 20, 5] };
const res = qstick_batch(open, close, cfg);

// res: { values: Float64Array, combos: Array<{ period?: number }>, rows: number, cols: number }
console.log(res.rows, res.cols);

// Extract first combo's series
const firstRow = res.values.slice(0, res.cols);

Performance Analysis

Comparison:
View:

Across sizes, Rust CPU runs about 1.01× slower than Tulip C in this benchmark.

Loading chart...

AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05

Related Indicators