Exponential Trend
exp_rate = 0.00003 | initial_distance = 4 | width_multiplier = 1 Overview
Exponential Trend is a multi-output overlay built around a long warmup and a stateful trend handoff. Internally it uses `ATR(14)` around `hl2` to form seeded upper and lower guides, waits until a long valid history has accumulated, then activates either an uptrend or downtrend base line. From that active base it projects an exponential extension whose spacing is widened further by an ATR-scaled width factor.
That produces six coordinated outputs: uptrend and downtrend base lines, uptrend and downtrend extension lines, and one-shot bullish or bearish change markers. Only the currently active side stays populated, while the inactive side remains `NaN`. The study always works from high, low, and close; there is no configurable candle source parameter.
Defaults: Exponential Trend uses `exp_rate = 0.00003`, `initial_distance = 4.0`, and `width_multiplier = 1.0`.
Implementation Examples
Compute all six trend outputs from HLC slices or from candle data.
use vector_ta::indicators::exponential_trend::{
exponential_trend,
ExponentialTrendInput,
ExponentialTrendParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};
let high = vec![101.0, 101.5, 102.2, 102.0, 102.7];
let low = vec![99.8, 100.4, 100.9, 101.1, 101.6];
let close = vec![100.5, 101.1, 101.8, 101.6, 102.3];
let output = exponential_trend(&ExponentialTrendInput::from_slices(
&high,
&low,
&close,
ExponentialTrendParams {
exp_rate: Some(0.00003),
initial_distance: Some(4.0),
width_multiplier: Some(1.0),
},
))?;
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = exponential_trend(&ExponentialTrendInput::with_default_candles(&candles))?;
println!("uptrend base = {:?}", output.uptrend_base.last());
println!("downtrend extension = {:?}", candle_output.downtrend_extension.last());
println!("bullish / bearish = {:?} / {:?}", candle_output.bullish_change.last(), candle_output.bearish_change.last()); API Reference
Input Methods ▼
// From candles
ExponentialTrendInput::from_candles(&Candles, ExponentialTrendParams)
-> ExponentialTrendInput
// From HLC slices
ExponentialTrendInput::from_slices(
&[f64],
&[f64],
&[f64],
ExponentialTrendParams,
) -> ExponentialTrendInput
// From candles with default parameters
ExponentialTrendInput::with_default_candles(&Candles)
-> ExponentialTrendInput Parameters Structure ▼
pub struct ExponentialTrendParams {
pub exp_rate: Option<f64>, // default 0.00003
pub initial_distance: Option<f64>, // default 4.0
pub width_multiplier: Option<f64>, // default 1.0
} Output Structure ▼
pub struct ExponentialTrendOutput {
pub uptrend_base: Vec<f64>,
pub downtrend_base: Vec<f64>,
pub uptrend_extension: Vec<f64>,
pub downtrend_extension: Vec<f64>,
pub bullish_change: Vec<f64>,
pub bearish_change: Vec<f64>,
} Validation, Warmup & NaNs ▼
- The input high, low, and close series must be non-empty and length-matched.
exp_rate,initial_distance, andwidth_multipliermust all be valid finite values inside the accepted bounds.- The implementation requires a contiguous valid run of
101bars, and one-shot outputs are NaN-prefixed through the first100bars. - Inactive trend-side outputs remain
NaN, and the change markers are only populated on crossover or crossunder bars. - Streaming returns
Noneon non-finite input and resets its internal state when that happens. - Before the long seed window is complete, streaming can still return a six-value tuple, but the fields are typically all
NaN.
Builder, Streaming & Batch APIs ▼
// Builder
ExponentialTrendBuilder::new()
.exp_rate(f64)
.initial_distance(f64)
.width_multiplier(f64)
.kernel(Kernel)
.apply(&Candles)
ExponentialTrendBuilder::new()
.apply_slices(&[f64], &[f64], &[f64])
ExponentialTrendBuilder::new()
.into_stream()
// Stream
ExponentialTrendStream::try_new(ExponentialTrendParams)
ExponentialTrendStream::update(f64, f64, f64)
-> Option<(f64, f64, f64, f64, f64, f64)>
ExponentialTrendStream::reset()
// Batch
ExponentialTrendBatchBuilder::new()
.exp_rate_range(start, end, step)
.initial_distance_range(start, end, step)
.width_multiplier_range(start, end, step)
.kernel(Kernel)
.apply(&Candles)
ExponentialTrendBatchBuilder::new()
.apply_slices(&[f64], &[f64], &[f64]) Error Handling ▼
pub enum ExponentialTrendError {
EmptyInputData,
AllValuesNaN,
InconsistentSliceLengths { high_len: usize, low_len: usize, close_len: usize },
InvalidExpRate { exp_rate: f64 },
InvalidInitialDistance { initial_distance: f64 },
InvalidWidthMultiplier { width_multiplier: f64 },
NotEnoughValidData { needed: usize, valid: usize },
OutputLengthMismatch { expected: usize, got: usize },
InvalidRange { start: String, end: String, step: String },
InvalidKernelForBatch(Kernel),
} Python Bindings
Python exposes a dict-returning single-run function, a streaming class, and a batch function. Both scalar and batch paths preserve the same six named outputs, with batch reshaping each one into a row-major matrix.
import numpy as np
from vector_ta import exponential_trend, exponential_trend_batch, ExponentialTrendStream
result = exponential_trend(
np.asarray(high_values, dtype=np.float64),
np.asarray(low_values, dtype=np.float64),
np.asarray(close_values, dtype=np.float64),
exp_rate=0.00003,
initial_distance=4.0,
width_multiplier=1.0,
kernel="auto",
)
print(result["uptrend_base"], result["bullish_change"])
stream = ExponentialTrendStream(exp_rate=0.00003, initial_distance=4.0, width_multiplier=1.0)
print(stream.update(high_values[-1], low_values[-1], close_values[-1]))
batch = exponential_trend_batch(
np.asarray(high_values, dtype=np.float64),
np.asarray(low_values, dtype=np.float64),
np.asarray(close_values, dtype=np.float64),
exp_rate_range=(0.00002, 0.00006, 0.00002),
initial_distance_range=(3.0, 5.0, 1.0),
width_multiplier_range=(1.0, 1.5, 0.25),
)
print(batch["uptrend_base"].shape, batch["downtrend_extension"].shape)
print(batch["exp_rates"], batch["initial_distances"], batch["width_multipliers"]) JavaScript/WASM Bindings
The WASM layer exposes object-returning scalar and batch wrappers plus low-level buffer-based exports. The scalar JavaScript path returns a plain object with all six series, while batch adds `combos`, `rows`, and `cols`.
import init, { exponential_trend_js, exponential_trend_batch } from "/pkg/vector_ta.js";
await init();
const out = exponential_trend_js(
highValues,
lowValues,
closeValues,
0.00003,
4.0,
1.0,
);
console.log(out.uptrend_base, out.downtrend_base, out.uptrend_extension, out.downtrend_extension, out.bullish_change, out.bearish_change);
const batch = exponential_trend_batch(highValues, lowValues, closeValues, {
exp_rate_range: [0.00002, 0.00006, 0.00002],
initial_distance_range: [3.0, 5.0, 1.0],
width_multiplier_range: [1.0, 1.5, 0.25],
});
console.log(batch.uptrend_base, batch.downtrend_extension, batch.bullish_change, batch.combos, batch.rows, batch.cols); CUDA Bindings (Rust)
Additional details for the CUDA bindings can be found inside the VectorTA repository.
Performance Analysis
Across sizes, Rust CPU runs about 1.14× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU)