Variable Index Dynamic Average (VIDYA)

Parameters: short_period = 2 | long_period = 5 | alpha = 0.2 (0.05–0.5)

Overview

Variable Index Dynamic Average adapts its smoothing characteristics dynamically based on market volatility by modulating an exponential moving average's alpha parameter. The indicator measures standard deviation over both short and long windows, then scales a base smoothing factor by the ratio of short term to long term volatility to create a volatility adjusted alpha value. During high volatility periods when short term deviation exceeds long term norms, the increased alpha makes VIDYA more responsive to price changes, allowing it to track rapid moves more closely. Conversely, in low volatility environments where price action is subdued, the reduced alpha increases smoothing to filter out noise and provide steadier trend signals. This adaptive mechanism helps traders maintain appropriate sensitivity across varying market conditions without manual parameter adjustments. The exponential update style preserves the smooth characteristics traders expect from moving averages while adding intelligent responsiveness that simpler indicators lack. Default parameters use a 2 period short window, 5 period long window, and base alpha of 0.2, calibrated to provide balanced adaptation for most intraday and swing trading applications.

Implementation Examples

Get started with VIDYA in a few lines:

use vectorta::indicators::vidya::{vidya, VidyaInput, VidyaParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with price data slice
let prices = vec![100.0, 101.0, 100.5, 102.0, 103.5, 102.8];
let params = VidyaParams { short_period: Some(2), long_period: Some(5), alpha: Some(0.2) };
let input = VidyaInput::from_slice(&prices, params);
let result = vidya(&input)?;

// Using with Candles data structure
// Quick and simple with default parameters (short=2, long=5, alpha=0.2; source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = VidyaInput::with_default_candles(&candles);
let result = vidya(&input)?;

// Access the VIDYA values
for value in result.values {
    println!("VIDYA: {}", value);
}

API Reference

Input Methods
// From price slice
VidyaInput::from_slice(&[f64], VidyaParams) -> VidyaInput

// From candles with custom source
VidyaInput::from_candles(&Candles, &str, VidyaParams) -> VidyaInput

// From candles with default params (close prices, 2/5, alpha=0.2)
VidyaInput::with_default_candles(&Candles) -> VidyaInput
Parameters Structure
pub struct VidyaParams {
    pub short_period: Option<usize>, // Default: 2 (min 2)
    pub long_period: Option<usize>,  // Default: 5 (≥ short, min 2)
    pub alpha: Option<f64>,          // Default: 0.2 (0.0..=1.0)
}
Output Structure
pub struct VidyaOutput {
    pub values: Vec<f64>, // Adaptive moving average values
}
Validation, Warmup & NaNs
  • short_period ≥ 2, long_period ≥ 2, and short_period ≤ long_period.
  • 0.0 ≤ alpha ≤ 1.0; otherwise VidyaError::InvalidParameters.
  • There must be at least long_period valid points after the first finite input; otherwise VidyaError::NotEnoughValidData.
  • Outputs before first + long_period − 2 are NaN. At that index, VIDYA is seeded to the price; next index updates with adaptive k.
  • If σlong = 0, the ratio is treated as 0 (factor k=0), so the value holds steady.
  • Streaming: returns None until long_period − 1 updates; first Some equals the current price, then adapts.
Error Handling
use vectorta::indicators::vidya::{vidya, VidyaError};

match vidya(&input) {
    Ok(output) => process_results(output.values),
    Err(VidyaError::EmptyData) =>
        eprintln!("Input data is empty"),
    Err(VidyaError::AllValuesNaN) =>
        eprintln!("All input values are NaN"),
    Err(VidyaError::NotEnoughValidData { needed, valid }) =>
        eprintln!("Need {} data points after first finite, only {} valid", needed, valid),
    Err(VidyaError::InvalidParameters { short, long, alpha }) =>
        eprintln!("Invalid params: short={}, long={}, alpha={}", short, long, alpha),
    Err(VidyaError::InvalidOutputLength { dst_len, data_len }) =>
        eprintln!("Output length {} ≠ input length {}", dst_len, data_len),
}

Python Bindings

Basic Usage

Calculate VIDYA using NumPy arrays (defaults: short=2, long=5, alpha=0.2):

import numpy as np
from vectorta import vidya

# Prepare price data as NumPy array
prices = np.array([100.0, 101.0, 100.5, 102.0, 103.5, 102.8])

# Calculate VIDYA with defaults (2/5, alpha=0.2)
result = vidya(prices, short_period=2, long_period=5, alpha=0.2)

# Or specify custom parameters and kernel
result = vidya(prices, short_period=3, long_period=10, alpha=0.25, kernel="avx2")

# Result is a NumPy array matching input length
print(f"VIDYA values: {result}")
Streaming Real-time Updates

Process real-time price updates efficiently:

from vectorta import VidyaStream

# Initialize streaming VIDYA calculator
stream = VidyaStream(short_period=2, long_period=5, alpha=0.2)

# Process real-time price updates
for price in price_feed:
    vidya_value = stream.update(price)

    if vidya_value is not None:
        # Value is ready (None during warmup period)
        print(f"Current VIDYA: {vidya_value}")
Batch Parameter Optimization

Test multiple parameter combinations:

import numpy as np
from vectorta import vidya_batch

# Your price data
prices = np.array([...])  # Your historical prices

# Define parameter ranges to test (start, end, step)
short_range = (2, 6, 2)   # 2, 4, 6
long_range  = (5, 15, 5)  # 5, 10, 15
alpha_range = (0.1, 0.3, 0.1)  # 0.1, 0.2, 0.3

# Run batch calculation
results = vidya_batch(
    prices,
    short_period_range=short_range,
    long_period_range=long_range,
    alpha_range=alpha_range,
    kernel="auto"
)

# Access results
values = results['values']            # shape: (num_combinations, len(prices))
shorts = results['short_periods']
longs  = results['long_periods']
alphas = results['alphas']
CUDA Acceleration

CUDA support for VIDYA is currently under development. The API will follow the same pattern as other CUDA-enabled indicators.

# Coming soon: CUDA-accelerated VIDYA calculations
# Patterns will mirror other CUDA-enabled indicators in this project.

JavaScript/WASM Bindings

Basic Usage

Calculate VIDYA in JavaScript/TypeScript:

import { vidya_js } from 'vectorta-wasm';

// Price data as Float64Array or regular array
const prices = new Float64Array([100.0, 101.0, 100.5, 102.0, 103.5, 102.8]);

// Calculate VIDYA with specified parameters
const result = vidya_js(prices, 2, 5, 0.2);  // short=2, long=5, alpha=0.2

// Result is a Float64Array
console.log('VIDYA values:', result);

// Use with async/await for better error handling
async function calculateVIDYA(prices: Float64Array): Promise<Float64Array> {
  try {
    return vidya_js(prices, 2, 5, 0.2);
  } catch (error) {
    console.error('VIDYA calculation failed:', error);
    throw error;
  }
}
Memory-Efficient Operations

Use zero-copy operations for better performance with large datasets:

import { vidya_alloc, vidya_free, vidya_into, memory } from 'vectorta-wasm';

// Prepare your price data
const prices = new Float64Array([/* your data */]);
const length = prices.length;

// Allocate WASM memory for input and output
const inPtr = vidya_alloc(length);
const outPtr = vidya_alloc(length);

// Copy input data into WASM memory
new Float64Array(memory.buffer, inPtr, length).set(prices);

// Calculate VIDYA directly into allocated memory
// Args: in_ptr, out_ptr, len, short_period, long_period, alpha
vidya_into(inPtr, outPtr, length, 2, 5, 0.2);

// Read results from WASM memory (slice() to copy out)
const vidyaValues = new Float64Array(memory.buffer, outPtr, length).slice();

// Free allocated memory when done
vidya_free(inPtr, length);
vidya_free(outPtr, length);

console.log('VIDYA values:', vidyaValues);
Batch Processing

Test multiple parameter combinations efficiently:

import { vidya_batch } from 'vectorta-wasm';

// Your price data
const prices = new Float64Array([/* historical prices */]);

// Define parameter sweep ranges
const config = {
  short_period_range: [2, 6, 2],   // 2, 4, 6
  long_period_range:  [5, 15, 5],  // 5, 10, 15
  alpha_range:        [0.1, 0.3, 0.1] // 0.1, 0.2, 0.3
};

// Calculate all combinations
const { values, combos, rows, cols } = vidya_batch(prices, config);

// values is a flat Float64Array of length rows*cols
// combos is an array of parameter structs ({ short_period, long_period, alpha })

// Reshape example: collect rows
const matrix: Float64Array[] = [];
for (let r = 0; r < rows; r++) {
  const start = r * cols;
  matrix.push(values.slice(start, start + cols));
}

console.log('Tested combos:', combos);
console.log('First-row VIDYA values:', matrix[0]);

Performance Analysis

Comparison:
View:

Across sizes, Rust CPU runs about 1.92× faster than Tulip C in this benchmark.

Loading chart...

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

CUDA note

In our benchmark workload, the Rust CPU implementation is faster than CUDA for this indicator. Prefer the Rust/CPU path unless your workload differs.

Related Indicators