Double Exponential Moving Average (DEMA)
period = 30 Overview
The Double Exponential Moving Average (DEMA), developed by Patrick Mulloy in 1994, reduces the lag inherent in traditional moving averages while maintaining smooth price tracking. The indicator calculates an exponential moving average of prices, then takes an EMA of that first EMA to create a double smoothed series. Rather than using this double smoothed value directly, DEMA combines both EMAs using the formula: 2 times EMA minus EMA of EMA. This mathematical approach cancels out much of the lag introduced by the smoothing process, creating a moving average that responds faster to price changes while filtering noise.
DEMA tracks price more closely than standard EMAs during trending markets, turning faster at inflection points and staying closer to current price action. The indicator maintains the smoothness characteristic of exponential averages while cutting lag approximately in half compared to a standard EMA of the same period. This responsiveness makes DEMA particularly effective for timing entries and exits in trending markets. The period parameter, typically set to 30, determines the lookback window for both EMA calculations. Shorter periods create more responsive averages suitable for scalping, while longer periods provide smoother signals better suited for position trading.
Traders use DEMA as a versatile tool for trend identification, signal generation, and dynamic support and resistance. Price crossovers with DEMA generate earlier signals than traditional moving averages, though with slightly more false signals during choppy markets. The indicator works particularly well in crossover systems where a fast DEMA crosses a slower one, providing timely trend change signals. Many traders also use DEMA as a trailing stop mechanism, as its reduced lag keeps stops closer to price during trends. DEMA serves as an excellent baseline for other indicators, providing a responsive yet stable price proxy for momentum calculations or band construction.
Implementation Examples
Compute DEMA from a slice or candles:
use vectorta::indicators::dema::{dema, DemaInput, DemaParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using a price slice
let prices = vec![100.0, 101.0, 102.5, 101.5, 103.0];
let params = DemaParams { period: Some(30) }; // default is 30
let input = DemaInput::from_slice(&prices, params);
let result = dema(&input)?;
// Using Candles with defaults (period=30, source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = DemaInput::with_default_candles(&candles);
let result = dema(&input)?;
// Access values
for v in result.values {
println!("DEMA: {}", v);
} API Reference
Input Methods ▼
// From price slice
DemaInput::from_slice(&[f64], DemaParams) -> DemaInput
// From candles with custom source
DemaInput::from_candles(&Candles, &str, DemaParams) -> DemaInput
// From candles with defaults (period=30, source="close")
DemaInput::with_default_candles(&Candles) -> DemaInput Parameters Structure ▼
pub struct DemaParams {
pub period: Option<usize>, // Default: 30
} Output Structure ▼
pub struct DemaOutput {
pub values: Vec<f64>, // DEMA values (2*EMA1 - EMA2)
} Validation, Warmup & NaNs ▼
period > 0andperiod ≤ len(data).- Data length must satisfy
needed = 2 × (period − 1); otherwiseDemaError::NotEnoughData. - After the first finite input, at least
neededvalid points are required; elseDemaError::NotEnoughValidData. - Batch output enforces warmup: indices
..(first + period − 1)areNaN. Streaming returnsNonefor the firstperiod − 1updates (period=1 emits immediately).
Error Handling ▼
use vectorta::indicators::dema::{dema, DemaError};
match dema(&input) {
Ok(output) => process(output.values),
Err(DemaError::EmptyInputData) => println!("Input data is empty"),
Err(DemaError::AllValuesNaN) => println!("All input values are NaN"),
Err(DemaError::InvalidPeriod { period, data_len }) =>
println!("Invalid period {} for length {}", period, data_len),
Err(DemaError::NotEnoughData { needed, valid }) =>
println!("Need {} points, only {} available", needed, valid),
Err(DemaError::NotEnoughValidData { needed, valid }) =>
println!("Need {} valid points after first finite, only {}", needed, valid),
} Python Bindings
Basic Usage ▼
Calculate DEMA using NumPy arrays (default period=30):
import numpy as np
from vectorta import dema
prices = np.array([100.0, 101.0, 102.5, 101.5, 103.0])
# With explicit period
result = dema(prices, period=30)
# Specify kernel for optimization ("auto" | "avx2" | "avx512" if available)
result = dema(prices, period=30, kernel="auto")
print(result) # NumPy array Streaming Real-time Updates ▼
Update an internal state and receive values after warmup:
from vectorta import DemaStream
stream = DemaStream(period=30)
for price in price_feed:
val = stream.update(price)
if val is not None:
handle(val) Batch Parameter Optimization ▼
Test multiple period values efficiently:
import numpy as np
from vectorta import dema_batch
prices = np.array([...])
results = dema_batch(
prices,
period_range=(10, 50, 10),
kernel="auto"
)
print(results['values'].shape) # (num_periods, len(prices))
print(results['periods']) # list of tested periods CUDA Acceleration ▼
CUDA helpers are available when built with Python + CUDA features:
# Single-series parameter sweep on GPU (f32)
from vectorta import dema_cuda_batch_dev
res = dema_cuda_batch_dev(
data_f32=prices.astype('float32'),
period_range=(10, 50, 10),
device_id=0
)
# Many series × one parameter (time-major [T, N])
from vectorta import dema_cuda_many_series_one_param_dev
out = dema_cuda_many_series_one_param_dev(
data_tm_f32=portfolio_tm.astype('float32'),
period=30,
device_id=0
) JavaScript/WASM Bindings
Basic Usage ▼
Compute DEMA in JavaScript/TypeScript:
import { dema_js } from 'vectorta-wasm';
const prices = new Float64Array([100.0, 101.0, 102.5, 101.5, 103.0]);
const values = dema_js(prices, 30);
console.log('DEMA values:', values); Memory-Efficient Operations ▼
Use zero-copy APIs for performance:
import { dema_alloc, dema_free, dema_into, memory } from 'vectorta-wasm';
const prices = new Float64Array([/* data */]);
const n = prices.length;
const inPtr = dema_alloc(n);
const outPtr = dema_alloc(n);
try {
new Float64Array(memory.buffer, inPtr, n).set(prices);
// Args: in_ptr, out_ptr, len, period
dema_into(inPtr, outPtr, n, 30);
const out = new Float64Array(memory.buffer, outPtr, n).slice();
console.log(out);
} finally {
dema_free(inPtr, n);
dema_free(outPtr, n);
} Batch Processing ▼
Run a unified batch over period values and get metadata:
import { dema_batch } from 'vectorta-wasm';
const prices = new Float64Array([/* historical prices */]);
const { values, combos, rows, cols } = dema_batch(prices, {
period_range: [10, 50, 10]
});
// Reshape values to a matrix [rows x cols]
const matrix: number[][] = [];
for (let r = 0; r < rows; r++) {
const start = r * cols;
matrix.push(Array.from(values.slice(start, start + cols)));
}
// Access the period for the first combo
const firstPeriod = (combos[0] as any).period; Performance Analysis
Across sizes, Rust CPU runs about 2.53× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05
Related Indicators
Arnaud Legoux Moving Average
Moving average indicator
Compound Ratio Moving Average (CoRa Wave)
Moving average indicator
Centered Weighted Moving Average
Moving average indicator
Ehlers Distance Coefficient Filter
Moving average indicator
Ehlers Error-Correcting EMA (ECEMA)
Moving average indicator
Ehlers Instantaneous Trendline
Moving average indicator