VIDYA — Variable Index Dynamic Average¶
Similar to KAMA but uses the Chande Momentum Oscillator as its efficiency measure. The three options control the short and long CMO periods and the base smoothing constant alpha.
Inputs: [real] | Options: [short_period, long_period, alpha] | Outputs: [vidya]
Basic¶
use tulip_rs::indicators::vidya::{Vidya, TIndicatorState, Indicator};
let close = vec![81.59, 81.06, 82.87, 83.00, 83.61,
83.15, 82.84, 83.99, 84.55, 84.36_f64];
// Options: [short_period, long_period, alpha]
let (outputs, _state) = Vidya::indicator(&[close.as_slice()], &[2.0, 5.0, 0.2], None).unwrap();
println!("VIDYA: {:?}", outputs[0]);
// State continuation
let partial = close[..8].to_vec();
let (outputs2, mut state) = Vidya::indicator(&[partial.as_slice()], &[2.0, 5.0, 0.2], None).unwrap();
println!("Partial VIDYA: {:?}", outputs2[0]);
let new_close = close[8..].to_vec();
let continued = state.batch_indicator(&[new_close.as_slice()], None).unwrap();
println!("Continued VIDYA: {:?}", continued[0]);
#include "tulip_rs_ffi.h"
double close[] = {81.59, 81.06, 82.87, 83.00, 83.61,
83.15, 82.84, 83.99, 84.55, 84.36};
double options[VIDYA_OPTIONS] = {2.0, 5.0, 0.2}; // short_period, long_period, alpha
const double *inputs[VIDYA_INPUTS] = {close};
/* Full computation (check r.error == C_INDICATOR_ERROR_OK in real code) */
CIndicatorResult r = vidya_indicator(inputs, 10, options, NULL, 0);
/* r.outputs[0] -> the VIDYA series, length r.output_lens[0] */
tulip_ffi_result_free(r);
vidya_state_free(r.state);
/* Partial computation + state continuation */
CIndicatorResult p = vidya_indicator(inputs, 8, options, NULL, 0);
double new_close[] = {84.55, 84.36};
const double *new_inputs[VIDYA_INPUTS] = {new_close};
CBatchResult b = vidya_batch(p.state, new_inputs, 2, NULL, 0);
/* b.outputs[0] -> VIDYA values for just the two new bars */
tulip_ffi_batch_result_free(b);
tulip_ffi_result_free(p);
vidya_state_free(p.state);
import "github.com/me60732/tulip_rs_go/indicators"
close := []float64{81.59, 81.06, 82.87, 83.00, 83.61,
83.15, 82.84, 83.99, 84.55, 84.36}
options := []float64{2.0, 5.0, 0.2} // short_period, long_period, alpha
// Full computation — Rows are zero-copy views, valid until Close.
res, st, _ := indicators.Vidya.Indicator(close, options, nil)
fmt.Println(res.Rows[0]) // VIDYA values
res.Close()
st.Close()
// Partial computation + state continuation.
res2, st2, _ := indicators.Vidya.Indicator(close[:8], options, nil)
res2.Close() // outputs consumed or closed; state stays live
batch, _ := st2.Batch(close[8:], nil)
fmt.Println(batch.Rows[0]) // continued VIDYA values
batch.Close()
st2.Close()
import org.tuliprs.*;
import org.tuliprs.indicators.Vidya;
double[] close = {81.59, 81.06, 82.87, 83.00, 83.61,
83.15, 82.84, 83.99, 84.55, 84.36};
double[] options = {2.0, 5.0, 0.2}; // short_period, long_period, alpha
// Full computation — output rows are zero-copy views, valid until close().
Outcome oc = Vidya.indicator(new double[][] {close}, options);
try (Result res = oc.result(); State st = oc.state()) {
System.out.println(java.util.Arrays.toString(res.toDoubleArray(0))); // VIDYA values
}
// Partial computation + state continuation.
int n = 8;
Outcome p = Vidya.indicator(
new double[][] {java.util.Arrays.copyOfRange(close, 0, n)}, options);
try (Result pr = p.result(); State st = p.state()) {
Result br = st.batch(
new double[][] {java.util.Arrays.copyOfRange(close, n, 10)});
try (br) {
System.out.println(java.util.Arrays.toString(br.toDoubleArray(0))); // continued VIDYA values
}
}
import numpy as np
import tulip_rs
close = np.array([81.59, 81.06, 82.87, 83.00, 83.61,
83.15, 82.84, 83.99, 84.55, 84.36], dtype=np.float64)
# Options: [short_period, long_period, alpha]
outputs, state = tulip_rs.indicators.vidya.indicator([close], [2.0, 5.0, 0.2])
print("VIDYA:", outputs[0])
# State continuation
partial = close[:8]
outputs2, state = tulip_rs.indicators.vidya.indicator([partial], [2.0, 5.0, 0.2])
new_close = close[8:]
continued = state.batch_indicator([new_close])
print("Continued VIDYA:", continued[0])
import * as ti from 'tulip-rs-node';
const close = Float64Array.from([81.59, 81.06, 82.87, 83.00, 83.61,
83.15, 82.84, 83.99, 84.55, 84.36,
85.53, 86.54, 86.89, 87.77, 87.29]);
const [outputs, state] = ti.vidya.indicator([close], [2, 5, 0.2]);
console.log('VIDYA:', outputs[0]);
// State continuation
const [, state2] = ti.vidya.indicator([close.slice(0, -5)], [2, 5, 0.2]);
const continued = state2.batchIndicator([close.slice(-5)]);
console.log('Continued VIDYA:', continued[0]);
import { init } from 'tulip-rs-wasm';
import * as ti from 'tulip-rs-wasm';
await init(); // bundler resolves the WASM asset automatically
const close = [81.59, 81.06, 82.87, 83.00, 83.61,
83.15, 82.84, 83.99, 84.55, 84.36,
85.53, 86.54, 86.89, 87.77, 87.29];
const [outputs, state] = ti.vidya.indicator([close], [2, 5, 0.2]);
console.log('VIDYA:', outputs[0]);
// State continuation
const [, state2] = ti.vidya.indicator([close.slice(0, -5)], [2, 5, 0.2]);
const continued = state2.batchIndicator([close.slice(-5)]);
console.log('Continued VIDYA:', continued[0]);
Optional Outputs¶
vidya exposes 4 optional outputs: "short_sma", "long_sma", "short_sdtdev", "long_sdtdev". Pass a boolean mask as the third argument — one bool per optional output, in order.
use tulip_rs::indicators::vidya::{Vidya, TIndicatorState, Indicator};
let close = vec![81.59, 81.06, 82.87, 83.00, 83.61,
83.15, 82.84, 83.99, 84.55, 84.36_f64];
// Request short_sma and long_sma but not the stddev outputs
let mask = [true, true, false, false]; // one per optional output
let (outputs, _state) = Vidya::indicator(&[close.as_slice()], &[5.0, 20.0, 0.2], Some(&mask)).unwrap();
let vidya = &outputs[0]; // vidya (primary)
let short_sma = &outputs[1]; // "short_sma" (optional — requested)
let long_sma = &outputs[2]; // "long_sma" (optional — requested)
// "short_sdtdev" not requested
// "long_sdtdev" not requested
vidya exposes 4 optional outputs: short_sma, long_sma, short_stddev, long_stddev. Pass a boolean mask — one bool per optional output.
#include "tulip_rs_ffi.h"
double close[] = {81.59, 81.06, 82.87, 83.00, 83.61,
83.15, 82.84, 83.99, 84.55, 84.36};
double options[VIDYA_OPTIONS] = {5.0, 20.0, 0.2}; // short_period, long_period, alpha
const double *inputs[VIDYA_INPUTS] = {close};
bool optional_outputs[] = {true, true, false, false}; // short_sma, long_sma, short_stddev, long_stddev
CIndicatorResult r = vidya_indicator(inputs, 10, options, optional_outputs, 4);
/* r.outputs[0] -> vidya (primary) */
/* r.outputs[1] -> short_sma (optional — requested) */
/* r.outputs[2] -> long_sma (optional — requested) */
tulip_ffi_result_free(r);
vidya_state_free(r.state);
vidya exposes 4 optional outputs: short_sma, long_sma, short_stddev, long_stddev. Pass a boolean mask — one bool per optional output.
import "github.com/me60732/tulip_rs_go/indicators"
close := []float64{81.59, 81.06, 82.87, 83.00, 83.61,
83.15, 82.84, 83.99, 84.55, 84.36}
options := []float64{5.0, 20.0, 0.2} // short_period, long_period, alpha
// Request short_sma and long_sma but not the stddev outputs
mask := []bool{true, true, false, false} // one per optional output
res, st, _ := indicators.Vidya.Indicator(close, options, mask)
vidya := res.Rows[0] // vidya (primary)
short_sma := res.Rows[1] // "short_sma" (optional — requested)
long_sma := res.Rows[2] // "long_sma" (optional — requested)
// "short_stddev" not requested
// "long_stddev" not requested
res.Close()
st.Close()
vidya exposes 4 optional outputs: short_sma, long_sma, short_stddev, long_stddev. Pass a boolean mask as the third argument — one boolean per optional output, in order.
import org.tuliprs.*;
import org.tuliprs.indicators.Vidya;
// (close series as in the Basic tab)
boolean[] mask = {true, true, false, false}; // short_sma, long_sma, short_stddev, long_stddev
Outcome oc = Vidya.indicator(new double[][] {close}, new double[] {5.0, 20.0, 0.2}, mask);
try (Result res = oc.result()) {
// row 0 = vidya (primary), row 1 = short_sma, row 2 = long_sma; stddev outputs not requested
System.out.println(java.util.Arrays.toString(res.toDoubleArray(1))); // short_sma
}
oc.state().close();
import numpy as np
import tulip_rs
close = np.array([81.59, 81.06, 82.87, 83.00, 83.61,
83.15, 82.84, 83.99, 84.55, 84.36], dtype=np.float64)
# Request short_sma and long_sma but not the stddev outputs
outputs, state = tulip_rs.indicators.vidya.indicator(
[close], [5.0, 20.0, 0.2],
optional_outputs=[True, True, False, False],
)
vidya = outputs[0] # vidya (primary)
short_sma = outputs[1] # "short_sma" (optional — requested)
long_sma = outputs[2] # "long_sma" (optional — requested)
# "short_sdtdev" not requested
# "long_sdtdev" not requested
vidya exposes 4 optional outputs: short_sma, long_sma, short_stddev, long_stddev.
// Request all optional outputs
const [allOut] = ti.vidya.indicator([close], [2, 5, 0.2], [true, true, true, true]);
const vidya = allOut[0]; // primary
const shortSma = allOut[1]; // optional 0: short_sma
const longSma = allOut[2]; // optional 1: long_sma
const shortStddev = allOut[3]; // optional 2: short_stddev
const longStddev = allOut[4]; // optional 3: long_stddev
// Request only the SMAs
const [partial] = ti.vidya.indicator([close], [2, 5, 0.2], [true, true, false, false]);
The WASM API is identical to Node.js — pass the boolean mask as the third argument.
// Request all optional outputs
const [allOut] = ti.vidya.indicator([close], [2, 5, 0.2], [true, true, true, true]);
const vidya = allOut[0]; // primary
const shortSma = allOut[1]; // optional 0: short_sma
const longSma = allOut[2]; // optional 1: long_sma
const shortStddev = allOut[3]; // optional 2: short_stddev
const longStddev = allOut[4]; // optional 3: long_stddev
// Request only the SMAs
const [partial] = ti.vidya.indicator([close], [2, 5, 0.2], [true, true, false, false]);
SIMD¶
By assets — same options applied to 4 assets in parallel:
use tulip_rs::indicators::vidya::{Vidya, Indicator};
let a1 = vec![81.59, 81.06, 82.87, 83.00, 83.61, 83.15, 82.84, 83.99, 84.55, 84.36_f64];
let a2 = vec![72.10, 72.85, 73.40, 73.00, 74.20, 74.85, 75.10, 75.60, 76.00, 76.50_f64];
let a3 = vec![55.30, 55.80, 56.10, 56.40, 56.90, 57.20, 57.50, 57.80, 58.10, 58.40_f64];
let a4 = vec![100.1, 100.5, 101.0, 101.3, 101.8, 102.0, 102.5, 103.0, 103.3, 103.8_f64];
let inputs: [&[&[f64]; 1]; 4] = [
&[a1.as_slice()],
&[a2.as_slice()],
&[a3.as_slice()],
&[a4.as_slice()],
];
let results = Vidya::indicator_by_assets::<4>(&inputs, &[2.0, 5.0, 0.2], None).unwrap();
for (i, asset_outputs) in results.iter().enumerate() {
println!("Asset {}: {:?}", i + 1, asset_outputs[0]);
}
By options — same asset, 4 different option sets in parallel:
use tulip_rs::indicators::vidya::{Vidya, IndicatorByOptions};
let close = vec![81.59, 81.06, 82.87, 83.00, 83.61,
83.15, 82.84, 83.99, 84.55, 84.36_f64];
let opts: [&[f64; 3]; 4] = [
&[2.0, 5.0, 0.2],
&[3.0, 7.0, 0.3],
&[4.0, 9.0, 0.4],
&[5.0, 11.0, 0.5],
];
let results = Vidya::indicator_by_options::<4>(&[close.as_slice()], &opts, None).unwrap();
for (i, opt_outputs) in results.iter().enumerate() {
println!("Option set {}: {:?}", i + 1, opt_outputs[0]);
}
By assets — same options applied to 4 assets in one call (N must be 2/4/8/16):
double a1[] = {81.59, 81.06, 82.87, 83.00, 83.61, 83.15, 82.84, 83.99, 84.55, 84.36};
double a2[] = {72.10, 72.85, 73.40, 73.00, 74.20, 74.85, 75.10, 75.60, 76.00, 76.50};
double a3[] = {55.30, 55.80, 56.10, 56.40, 56.90, 57.20, 57.50, 57.80, 58.10, 58.40};
double a4[] = {100.1, 100.5, 101.0, 101.3, 101.8, 102.0, 102.5, 103.0, 103.3, 103.8};
/* one [INPUTS]-long pointer array per asset */
const double *asset1[VIDYA_INPUTS] = {a1};
const double *asset2[VIDYA_INPUTS] = {a2};
const double *asset3[VIDYA_INPUTS] = {a3};
const double *asset4[VIDYA_INPUTS] = {a4};
const double *const *const simd_inputs[4] = {asset1, asset2, asset3, asset4};
CSimdResult r = vidya_simd_by_assets(simd_inputs, 4, 10, options, NULL, 0);
for (uintptr_t i = 0; i < r.num_results; i++) {
/* r.outputs[i][0] -> asset i's series, length r.output_lens[i][0] */
vidya_state_free(r.states[i]);
}
tulip_ffi_simd_result_free(r);
By options — same asset, 4 different option sets in one call:
double o1[] = {1.0, 2.5, 0.1};
double o2[] = {2.0, 5.0, 0.2};
double o3[] = {3.0, 7.5, 0.3};
double o4[] = {4.0, 10.0, 0.4};
const double *const simd_opts[4] = {o1, o2, o3, o4};
CSimdResult r = vidya_simd_by_options(inputs, 10, simd_opts, 4, NULL, 0);
/* r.outputs[i] -> results for option set i */
for (uintptr_t i = 0; i < r.num_results; i++) vidya_state_free(r.states[i]);
tulip_ffi_simd_result_free(r);
By assets — same options applied to 4 assets in parallel (lane counts 2/4/8/16):
a1 := []float64{81.59, 81.06, 82.87, 83.00, 83.61, 83.15, 82.84, 83.99, 84.55, 84.36}
// Reuse the same data for assets 2–4 in this example
a2, a3, a4 := a1, a1, a1
assets := [][indicators.VidyaInputs][]float64{{a1}, {a2}, {a3}, {a4}}
sim, _ := indicators.Vidya.SimdByAssets(assets, []float64{2.0, 5.0, 0.2}, nil)
for i, lanes := range sim.Results {
fmt.Printf("Asset %d: %v\n", i+1, lanes[0])
}
sim.Close() // frees every lane state, then the SIMD buffers
By options — same asset, 4 different option sets in parallel (3-option indicator):
close := []float64{81.59, 81.06, 82.87, 83.00, 83.61,
83.15, 82.84, 83.99, 84.55, 84.36}
sim2, _ := indicators.Vidya.SimdByOptions(close, [][]float64{{1.0, 2.5, 0.1}, {2.0, 5.0, 0.2}, {3.0, 7.5, 0.3}, {4.0, 10.0, 0.4}}, nil)
for i, lanes := range sim2.Results {
fmt.Printf("Option set %d: %v\n", i+1, lanes[0])
}
sim2.Close()
By assets — same options applied to 4 assets in parallel (N must be 2, 4, 8, or 16):
import org.tuliprs.*;
import org.tuliprs.indicators.Vidya;
double[] a1 = {81.59, 81.06, 82.87, 83.00, 83.61, 83.15, 82.84, 83.99, 84.55, 84.36};
double[] a2 = {72.10, 72.85, 73.40, 73.00, 74.20, 74.85, 75.10, 75.60, 76.00, 76.50};
double[] a3 = {55.30, 55.80, 56.10, 56.40, 56.90, 57.20, 57.50, 57.80, 58.10, 58.40};
double[] a4 = {100.1, 100.5, 101.0, 101.3, 101.8, 102.0, 102.5, 103.0, 103.3, 103.8};
// One entry per asset; each asset lists its INPUTS series.
double[][][] assets = {{a1}, {a2}, {a3}, {a4}};
try (SimdResult sim = Vidya.simdByAssets(assets, new double[] {2.0, 5.0, 0.2}, null)) {
for (int i = 0; i < sim.numResults(); i++) {
System.out.printf("Asset %d: %s%n", i + 1,
java.util.Arrays.toString(sim.toDoubleArray(i, 0)));
}
} // frees every lane state, then the SIMD buffers (contractual order)
By options — same asset, 4 different option sets in parallel:
import org.tuliprs.*;
import org.tuliprs.indicators.Vidya;
double[] close = {81.59, 81.06, 82.87, 83.00, 83.61,
83.15, 82.84, 83.99, 84.55, 84.36};
try (SimdResult sim = Vidya.simdByOptions(new double[][] {close},
new double[][] {{1.0, 2.5, 0.1}, {2.0, 5.0, 0.2}, {3.0, 7.5, 0.3}, {4.0, 10.0, 0.4}}, null)) {
for (int i = 0; i < sim.numResults(); i++) {
System.out.printf("Option set %d: %s%n", i + 1,
java.util.Arrays.toString(sim.toDoubleArray(i, 0)));
}
}
By assets — same options applied to N assets in parallel (must be 2, 4, 8, or 16):
import numpy as np
import tulip_rs
close = np.array([81.59, 81.06, 82.87, 83.00, 83.61,
83.15, 82.84, 83.99, 84.55, 84.36], dtype=np.float64)
simd_inputs = [[close], [close + 5.0], [close - 5.0], [close * 1.02]]
outputs_list, states = tulip_rs.indicators.vidya.simd_by_assets(simd_inputs, [2.0, 5.0, 0.2])
for i, out in enumerate(outputs_list):
print(f"Asset {i + 1}: {out[0]}")
By options — same asset, N different option sets in parallel:
import numpy as np
import tulip_rs
close = np.array([81.59, 81.06, 82.87, 83.00, 83.61,
83.15, 82.84, 83.99, 84.55, 84.36], dtype=np.float64)
simd_options = [
[2.0, 5.0, 0.2],
[3.0, 7.0, 0.3],
[4.0, 9.0, 0.4],
[5.0, 11.0, 0.5],
]
outputs_list, states = tulip_rs.indicators.vidya.simd_by_options([close], simd_options)
for i, out in enumerate(outputs_list):
print(f"Option set {i + 1}: {out[0]}")
By assets — same options applied to 4 assets in parallel:
const simdInputs = [
[close.slice()],
[close.map(v => v * 1.1)],
[close.map(v => v * 0.9)],
[close.map(v => v * 1.02)],
];
const [results] = ti.vidya.simdByAssets(simdInputs, [2, 5, 0.2]);
results.forEach((out, i) => console.log(`Asset ${i + 1}:`, out[0]));
By options — same asset, 4 different option sets in parallel: