Impulse MACD
length_ma = 34 | length_signal = 9 Overview
Impulse MACD combines a zero-centered impulse line with a signal line and histogram. For each bar, the implementation starts from typical price, smooths high and low with SMMA(length_ma), smooths the typical price twice with EMA(length_ma), and forms an impulse midpoint from that double-smoothed structure. The impulse_macd output is the distance from that midpoint to the smoothed high or low band when the midpoint pushes outside the envelope, otherwise 0.0. The signal output is an SMA of impulse_macd over length_signal, and impulse_histo is the difference between impulse_macd and signal once the signal line is warmed up.
Defaults: length_ma = 34 and length_signal = 9. Candle input uses built-in high, low, and close fields directly; there is no separate source selector.
Implementation Examples
Calculate Impulse MACD from slices or candles:
use vector_ta::indicators::impulse_macd::{
impulse_macd, ImpulseMacdInput, ImpulseMacdParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};
let high = vec![101.0, 102.5, 103.2, 104.0, 105.4];
let low = vec![99.4, 100.3, 101.1, 101.8, 103.0];
let close = vec![100.2, 101.8, 102.7, 103.4, 104.9];
let input = ImpulseMacdInput::from_slices(
&high,
&low,
&close,
ImpulseMacdParams::default(),
);
let out = impulse_macd(&input)?;
// Candles use built-in high/low/close fields
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let out_from_candles = impulse_macd(&ImpulseMacdInput::with_default_candles(&candles))?;
for ((md, hist), signal) in out
.impulse_macd
.iter()
.zip(out.impulse_histo.iter())
.zip(out.signal.iter())
{
println!("md={:.4}, hist={:.4}, signal={:.4}", md, hist, signal);
} API Reference
Input Methods ▼
// From candles (uses candles.high, candles.low, candles.close)
ImpulseMacdInput::from_candles(&Candles, ImpulseMacdParams) -> ImpulseMacdInput
// From explicit OHLC slices
ImpulseMacdInput::from_slices(&[f64], &[f64], &[f64], ImpulseMacdParams) -> ImpulseMacdInput
// From candles with default params
ImpulseMacdInput::with_default_candles(&Candles) -> ImpulseMacdInput Parameters and Outputs ▼
pub struct ImpulseMacdParams {
pub length_ma: Option<usize>, // default 34
pub length_signal: Option<usize>, // default 9
}
pub struct ImpulseMacdOutput {
pub impulse_macd: Vec<f64>,
pub impulse_histo: Vec<f64>,
pub signal: Vec<f64>,
} Builder, Stream, and Batch Types ▼
ImpulseMacdBuilder::new()
.length_ma(usize)
.length_signal(usize)
.kernel(Kernel)
.apply(&Candles) -> Result<ImpulseMacdOutput, ImpulseMacdError>
ImpulseMacdBuilder::new()
.length_ma(usize)
.length_signal(usize)
.kernel(Kernel)
.apply_slices(&[f64], &[f64], &[f64]) -> Result<ImpulseMacdOutput, ImpulseMacdError>
ImpulseMacdBuilder::new()
.length_ma(usize)
.length_signal(usize)
.into_stream() -> Result<ImpulseMacdStream, ImpulseMacdError>
ImpulseMacdStream::try_new(ImpulseMacdParams) -> Result<ImpulseMacdStream, ImpulseMacdError>
ImpulseMacdStream::update(high, low, close) -> Option<(f64, f64, f64)>
ImpulseMacdStream::update_reset_on_nan(high, low, close) -> Option<(f64, f64, f64)>
ImpulseMacdBatchBuilder::new()
.kernel(Kernel)
.length_ma_range(start, end, step)
.length_signal_range(start, end, step)
.length_ma_static(value)
.length_signal_static(value)
.apply_slices(&[f64], &[f64], &[f64]) -> Result<ImpulseMacdBatchOutput, ImpulseMacdError>
ImpulseMacdBatchBuilder::new()
.apply_candles(&Candles) -> Result<ImpulseMacdBatchOutput, ImpulseMacdError> Calculation, Warmup, and NaN Behavior ▼
- Valid bars require finite
high,low, andclose, and alsohigh >= low. - The implementation requires non-empty equal-length OHLC inputs plus
1 <= length_ma,length_signal <= data_len. - It finds the first valid bar, then requires at least
max(length_ma, length_signal)valid bars from that point or returnsImpulseMacdError::NotEnoughValidData. impulse_macdis NaN before the first valid bar. After that, bars can still be0.0while the SMMA high/low channel is not ready.signalandimpulse_histoare NaN untillength_signalvalid impulse values have accumulated, so the warmup boundary isfirst_valid + length_signal - 1.- Batch and slice paths call
update_reset_on_nan, so any invalid bar resets internal state and writes NaN at that index before the next valid segment starts over. - The core formula is
src = (high + low + close) / 3,mi = ema1 + (ema1 - ema2), thenimpulse_macd = mi - hi_smmaifmi > hi_smma,mi - lo_smmaifmi < lo_smma, else0.0.
Batch Output and Errors ▼
pub struct ImpulseMacdBatchOutput {
pub impulse_macd: Vec<f64>,
pub impulse_histo: Vec<f64>,
pub signal: Vec<f64>,
pub combos: Vec<ImpulseMacdParams>,
pub rows: usize,
pub cols: usize,
}
// Common errors:
// - EmptyInputData
// - DataLengthMismatch
// - AllValuesNaN
// - InvalidLengthMa / InvalidLengthSignal
// - NotEnoughValidData
// - OutputLengthMismatch
// - InvalidRange
// - InvalidKernelForBatch Python Bindings
Basic Usage ▼
The Python function returns three NumPy arrays in order:
import numpy as np
from vector_ta import impulse_macd
high = np.array([101.0, 102.5, 103.2, 104.0, 105.4], dtype=np.float64)
low = np.array([99.4, 100.3, 101.1, 101.8, 103.0], dtype=np.float64)
close = np.array([100.2, 101.8, 102.7, 103.4, 104.9], dtype=np.float64)
impulse_macd_values, impulse_histo, signal = impulse_macd(
high,
low,
close,
length_ma=34,
length_signal=9,
kernel="auto",
)
print(impulse_macd_values.shape, impulse_histo.shape, signal.shape) Streaming Real-time Updates ▼
The Python stream wrapper resets automatically on invalid bars:
from vector_ta import ImpulseMacdStream
stream = ImpulseMacdStream(length_ma=34, length_signal=9)
for high, low, close in live_feed:
result = stream.update(high, low, close)
if result is not None:
impulse_macd_value, impulse_histo_value, signal_value = result
print(impulse_macd_value, impulse_histo_value, signal_value) Batch Processing ▼
Batch output keys match the Rust batch contract plus parameter columns:
import numpy as np
from vector_ta import impulse_macd_batch
result = impulse_macd_batch(
high,
low,
close,
length_ma_range=(21, 55, 7),
length_signal_range=(9, 9, 0),
kernel="auto",
)
md = result["impulse_macd"]
hist = result["impulse_histo"]
signal = result["signal"]
length_mas = result["length_mas"]
length_signals = result["length_signals"]
rows = result["rows"]
cols = result["cols"] JavaScript/WASM Bindings
Basic Usage ▼
The WASM function returns a plain object with three arrays:
import { impulse_macd_js } from 'vectorta-wasm';
const high = new Float64Array([101.0, 102.5, 103.2, 104.0, 105.4]);
const low = new Float64Array([99.4, 100.3, 101.1, 101.8, 103.0]);
const close = new Float64Array([100.2, 101.8, 102.7, 103.4, 104.9]);
const result = impulse_macd_js(high, low, close, 34, 9) as {
impulse_macd: number[];
impulse_histo: number[];
signal: number[];
};
console.log(result.impulse_macd);
console.log(result.impulse_histo);
console.log(result.signal); Host Buffer API ▼
Low-level output buffers are packed as impulse_macd, then impulse_histo, then signal:
import {
impulse_macd_alloc,
impulse_macd_free,
impulse_macd_into_host,
} from 'vectorta-wasm';
const len = close.length;
const ptr = impulse_macd_alloc(len);
try {
impulse_macd_into_host(high, low, close, ptr, 34, 9);
// Read 3 * len float64 values from WASM memory:
// [impulse_macd..., impulse_histo..., signal...]
} finally {
impulse_macd_free(ptr, len);
} Batch Processing ▼
Batch config uses two 3-element ranges: [start, end, step].
import { impulse_macd_batch_js } from 'vectorta-wasm';
const batch = impulse_macd_batch_js(high, low, close, {
length_ma_range: [21, 55, 7],
length_signal_range: [9, 9, 0],
}) as {
impulse_macd: number[];
impulse_histo: number[];
signal: number[];
rows: number;
cols: number;
combos: Array<{ length_ma?: number; length_signal?: number }>;
};
console.log(batch.rows, batch.cols);
console.log(batch.combos[0]); 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)