EMD Trend
source = close | avg_type = SMA | length = 28 | mult = 1 Overview
EMD Trend is a band-based trend classifier rather than a simple overlay line. It first builds a center line from the selected source and moving-average family, then measures the absolute distance between price and that center line and smooths the distance with an EMA. That smoothed deviation becomes the basis for upper and lower bands, while a separate direction series flips to `1` when price breaks above the upper band and to `-1` when price breaks below the lower band.
The page therefore documents four outputs together: `direction`, `average`, `upper`, and `lower`. The average can come from several supported moving-average families, and the source can be any of the common candle composites such as `hl2`, `hlc3`, `ohlc4`, or `hlcc4`. In the default configuration the study runs on `close` with an SMA center line, but the batch and streaming paths keep the same four-output structure.
Defaults: EMD Trend uses `source = "close"`, `avg_type = "SMA"`, `length = 28`, and `mult = 1.0`.
Implementation Examples
Compute the direction state, center line, and bands from explicit OHLC slices.
use vector_ta::indicators::emd_trend::{
emd_trend,
EmdTrendInput,
EmdTrendParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};
let open = vec![100.0, 100.5, 101.1, 101.4, 102.0];
let high = vec![101.0, 101.3, 101.9, 102.2, 102.8];
let low = vec![99.7, 100.1, 100.8, 101.0, 101.6];
let close = vec![100.6, 101.0, 101.4, 102.0, 102.5];
let output = emd_trend(&EmdTrendInput::from_slices(
&open,
&high,
&low,
&close,
EmdTrendParams {
source: Some("close".to_string()),
avg_type: Some("SMA".to_string()),
length: Some(28),
mult: Some(1.0),
},
))?;
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = emd_trend(&EmdTrendInput::with_default_candles(&candles))?;
println!("direction = {:?}", output.direction.last());
println!("average = {:?}", candle_output.average.last());
println!("upper/lower = {:?} / {:?}", candle_output.upper.last(), candle_output.lower.last()); API Reference
Input Methods ▼
// From candles
EmdTrendInput::from_candles(&Candles, EmdTrendParams) -> EmdTrendInput
// From OHLC slices
EmdTrendInput::from_slices(
&[f64],
&[f64],
&[f64],
&[f64],
EmdTrendParams,
) -> EmdTrendInput
// From candles with default params
EmdTrendInput::with_default_candles(&Candles) -> EmdTrendInput Parameters Structure ▼
pub struct EmdTrendParams {
pub source: Option<String>, // default "close"
pub avg_type: Option<String>, // default "SMA"
pub length: Option<usize>, // default 28
pub mult: Option<f64>, // default 1.0
} Output Structure ▼
pub struct EmdTrendOutput {
pub direction: Vec<f64>,
pub average: Vec<f64>,
pub upper: Vec<f64>,
pub lower: Vec<f64>,
} Validation, Warmup & NaNs ▼
- All four OHLC inputs must be non-empty and length-matched.
sourcemust be one of the supported source strings such asclose,hl2,hlc3,ohlc4, orhlcc4.avg_typemust be one ofSMA,EMA,HMA,DEMA,TEMA,RMA, orFRAMA.lengthmust be positive andmultmust be finite and at least0.05.- The implementation requires a sufficiently long contiguous finite run; otherwise it returns
NotEnoughValidData. - The direction output starts at
0.0until an upper-band or lower-band break flips it to1.0or-1.0. - Streaming can return a direction with
NaNbands early, before enough history exists to compute the full trend envelope.
Builder, Streaming & Batch APIs ▼
// Single-run configuration uses params on EmdTrendInput
emd_trend(&EmdTrendInput::from_slices(..., EmdTrendParams { ... }))
// Stream
EmdTrendStream::try_new(EmdTrendParams)
EmdTrendStream::update(f64, f64, f64, f64)
-> Option<(f64, f64, f64, f64)>
// Batch builder
EmdTrendBatchBuilder::new()
.source(&str)?
.avg_type(&str)?
.length_range(start, end, step)
.mult_range(start, end, step)
.kernel(Kernel)
.apply_slices(&[f64], &[f64], &[f64], &[f64])
EmdTrendBatchBuilder::new()
.apply_candles(&Candles) Error Handling ▼
pub enum EmdTrendError {
EmptyInputData,
InputLengthMismatch { open_len: usize, high_len: usize, low_len: usize, close_len: usize },
AllValuesNaN,
InvalidSource { value: String },
InvalidAvgType { value: String },
InvalidLength { length: usize },
InvalidMult { mult: f64 },
NotEnoughValidData { needed: usize, valid: usize },
OutputLengthMismatch { expected: usize, got: usize },
MismatchedOutputLen { dst_len: usize, expected_len: usize },
InvalidRange { start: String, end: String, step: String },
InvalidKernelForBatch(Kernel),
InvalidInput { msg: String },
} Python Bindings
Python exposes a four-array single-run function, a streaming class, and a batch function. Both the scalar and batch paths preserve the same four-output structure: direction, average, upper, and lower.
import numpy as np
from vector_ta import emd_trend, emd_trend_batch, EmdTrendStream
direction, average, upper, lower = emd_trend(
np.asarray(open_values, dtype=np.float64),
np.asarray(high_values, dtype=np.float64),
np.asarray(low_values, dtype=np.float64),
np.asarray(close_values, dtype=np.float64),
source="close",
avg_type="SMA",
length=28,
mult=1.0,
kernel="auto",
)
stream = EmdTrendStream(source="close", avg_type="SMA", length=28, mult=1.0)
print(stream.update(open_values[-1], high_values[-1], low_values[-1], close_values[-1]))
batch = emd_trend_batch(
np.asarray(open_values, dtype=np.float64),
np.asarray(high_values, dtype=np.float64),
np.asarray(low_values, dtype=np.float64),
np.asarray(close_values, dtype=np.float64),
length_range=(20, 40, 10),
mult_range=(1.0, 2.0, 0.5),
source="close",
avg_type="SMA",
kernel="auto",
)
print(batch["direction"].shape, batch["average"].shape)
print(batch["lengths"], batch["mults"], batch["rows"], batch["cols"]) JavaScript/WASM Bindings
The WASM layer exposes object-returning scalar and batch wrappers plus lower-level buffer-based exports. The standard JavaScript path returns a plain object with `direction`, `average`, `upper`, and `lower` arrays. Batch adds `rows`, `cols`, and the tested parameter combos.
import init, { emd_trend_js, emd_trend_batch_js } from "/pkg/vector_ta.js";
await init();
const out = emd_trend_js(
openValues,
highValues,
lowValues,
closeValues,
"close",
"SMA",
28,
1.0,
);
console.log(out.direction, out.average, out.upper, out.lower);
const batch = emd_trend_batch_js(openValues, highValues, lowValues, closeValues, {
length_range: [20, 40, 10],
mult_range: [1.0, 2.0, 0.5],
source: "close",
avg_type: "SMA",
});
console.log(batch.direction, batch.average, batch.upper, batch.lower, batch.rows, batch.cols, batch.combos); 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)