Dickson Moving Average (DMA)
hull_length = 7 | ema_length = 20 | ema_gain_limit = 50 | hull_ma_type = WMA Overview
The Dickson Moving Average (DMA) creates a sophisticated hybrid indicator by combining an adaptive exponential moving average with a Hull style smoother to achieve both responsiveness and stability. The adaptive component dynamically adjusts its gain parameter by testing multiple values and selecting the one that minimizes prediction error at each step. This optimization occurs within a quantized grid of gain values, typically testing increments of 0.1 up to a specified limit. Meanwhile, the Hull component uses either weighted or exponential moving averages in a dual stage configuration to reduce lag while maintaining smoothness. The final DMA value averages these two distinct approaches, balancing the adaptivity of the error minimizing EMA with the stability of the Hull smoother.
DMA excels at tracking price movements with minimal delay while avoiding the overshooting common in highly responsive averages. The adaptive EMA component ensures the indicator adjusts to changing market conditions, becoming more reactive during trends and more stable during consolidation. The Hull smoothing component filters out noise and provides a stable baseline that prevents erratic behavior even when the adaptive component becomes aggressive. This dual nature makes DMA particularly effective in markets that alternate between trending and ranging phases. The gain limit parameter controls how adaptive the indicator can become, with higher limits allowing more aggressive tracking at the cost of potential instability.
Traders use DMA as a versatile tool for trend identification, signal generation, and dynamic support and resistance levels. The indicator generates cleaner crossover signals than traditional moving averages due to its adaptive nature and reduced lag. DMA works particularly well in trend following systems where its responsiveness helps capture moves early while the smoothing component reduces false signals. Many traders employ DMA as a trailing stop mechanism, as it stays close to price during trends but provides adequate distance during volatile periods. The indicator also serves as an excellent baseline for oscillators or bands, providing a responsive yet stable center line that adapts to market conditions.
Implementation Examples
Compute DMA from a slice or Candles:
use vectorta::indicators::moving_averages::dma::{dma, DmaInput, DmaParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// From price slice
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = DmaParams { hull_length: Some(7), ema_length: Some(20), ema_gain_limit: Some(50), hull_ma_type: Some("WMA".into()) };
let input = DmaInput::from_slice(&prices, params);
let result = dma(&input)?; // DmaOutput { values }
// From Candles with defaults (close, 7/20/50, WMA)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = DmaInput::with_default_candles(&candles);
let result = dma(&input)?;
for v in result.values { println!("DMA: {}", v); } API Reference
Input Methods ▼
// From price slice
DmaInput::from_slice(&[f64], DmaParams) -> DmaInput
// From candles with custom source
DmaInput::from_candles(&Candles, &str, DmaParams) -> DmaInput
// From candles with defaults (close, 7/20/50, "WMA")
DmaInput::with_default_candles(&Candles) -> DmaInput Parameters Structure ▼
pub struct DmaParams {
pub hull_length: Option<usize>, // Default: 7
pub ema_length: Option<usize>, // Default: 20
pub ema_gain_limit: Option<usize>, // Default: 50
pub hull_ma_type: Option<String>, // Default: "WMA" ("EMA" allowed)
} Output Structure ▼
pub struct DmaOutput {
pub values: Vec<f64>, // Final DMA values (length = input length)
} Validation, Warmup & NaNs ▼
hull_length > 0andema_length > 0; both must not exceed data length.- First finite input index =
first. Requires at leastmax(hull_length, ema_length) + ⌊√hull_length⌉valid points afterfirst; elseDmaError::NotEnoughValidData. hull_ma_type ∈ {"WMA", "EMA"}; otherwiseDmaError::InvalidHullMAType.- Outputs before warmup end are set to
NaN(prefix seeded to match batch/stream parity).
Error Handling ▼
#[derive(Debug, Error)]
pub enum DmaError {
EmptyInputData,
AllValuesNaN,
InvalidPeriod { period: usize, data_len: usize },
NotEnoughValidData { needed: usize, valid: usize },
InvalidHullMAType { value: String },
}
// Example handling
match dma(&input) {
Ok(out) => { /* use out.values */ }
Err(DmaError::EmptyInputData) => eprintln!("empty input"),
Err(DmaError::AllValuesNaN) => eprintln!("all NaN"),
Err(DmaError::InvalidPeriod { period, data_len }) => { /* fix params */ }
Err(DmaError::NotEnoughValidData { needed, valid }) => { /* wait for more data */ }
Err(DmaError::InvalidHullMAType { value }) => { /* use "WMA" or "EMA" */ }
}~ Python Bindings
Basic Usage ▼
Compute DMA and use the streaming class:
import numpy as np
from vectorta import dma, DmaStream, dma_batch
prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5])
# Vector DMA
values = dma(prices, hull_length=7, ema_length=20, ema_gain_limit=50, hull_ma_type="WMA", kernel=None)
# Streaming DMA
stream = DmaStream(7, 20, 50, "WMA")
for p in prices:
v = stream.update(p)
if v is not None:
print("DMA:", v) Batch Parameter Optimization ▼
Sweep hull/EMA/gain ranges and inspect combinations:
import numpy as np
from vectorta import dma_batch
prices = np.array([...], dtype=np.float64)
result = dma_batch(
prices,
hull_length_range=(3, 10, 1),
ema_length_range=(10, 30, 5),
ema_gain_limit_range=(10, 50, 10),
hull_ma_type="WMA",
kernel=None,
)
# result keys: values (rows×cols), hull_lengths, ema_lengths, ema_gain_limits, hull_ma_type, hull_ma_types
values = result["values"]
rows, cols = values.shape
print("rows, cols:", rows, cols)
print("hulls:", result["hull_lengths"]) CUDA Acceleration ▼
CUDA dev APIs are available when built with CUDA + Python features:
# from vectorta import dma_cuda_batch_dev, dma_cuda_many_series_one_param_dev
#
# # One series, many parameter combinations (device array result)
# out_dev = dma_cuda_batch_dev(
# data_f32=prices.astype(np.float32),
# hull_length_range=(3, 8, 1),
# ema_length_range=(10, 20, 2),
# ema_gain_limit_range=(10, 50, 10),
# hull_ma_type="WMA",
# device_id=0,
# )
#
# # Many series (T×N), one parameter set (device array result)
# out_dev = dma_cuda_many_series_one_param_dev(
# data_tm_f32=portfolio_tm.astype(np.float32),
# hull_length=7,
# ema_length=20,
# ema_gain_limit=50,
# hull_ma_type="WMA",
# device_id=0,
# ) JavaScript/WASM Bindings
Basic Usage ▼
Calculate DMA in JavaScript/TypeScript:
import { dma_js } from 'vectorta-wasm';
const prices = new Float64Array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5]);
const values = dma_js(prices, 7, 20, 50, "WMA");
console.log('DMA values:', values); Memory-Efficient Operations ▼
Use zero-copy into preallocated WASM buffers:
import { dma_alloc, dma_free, dma_into, memory } from 'vectorta-wasm';
const prices = new Float64Array([/* ... */]);
const n = prices.length;
const inPtr = dma_alloc(n);
const outPtr = dma_alloc(n);
new Float64Array(memory.buffer, inPtr, n).set(prices);
dma_into(inPtr, outPtr, n, 7, 20, 50, "WMA");
const out = new Float64Array(memory.buffer, outPtr, n).slice();
dma_free(inPtr, n);
dma_free(outPtr, n); Batch Processing ▼
Compute many parameter combinations at once:
import { dma_batch } from 'vectorta-wasm';
const prices = new Float64Array([/* ... */]);
const config = {
hull_length_range: [3, 10, 1],
ema_length_range: [10, 30, 5],
ema_gain_limit_range: [10, 50, 10],
hull_ma_type: "WMA",
};
const out = dma_batch(prices, config);
// out: { values: Float64Array, combos: DmaParams[], rows: number, cols: number }
console.log(out.rows, out.cols); Performance Analysis
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.