Moving Average Convergence/Divergence (MACD)

Parameters: fast_period = 12 | slow_period = 26 | signal_period = 9 | ma_type = ema

Overview

Moving Average Convergence Divergence, created by Gerald Appel in 1979, reveals changes in trend strength, direction, and momentum by calculating the difference between a 12 period EMA and a 26 period EMA, then smoothing this MACD line with a 9 period EMA signal line. The original parameter choice reflected market timing, with 12 representing two trading weeks, 26 representing one month, and 9 representing one and a half weeks when markets operated six days per week. Thomas Aspray enhanced the indicator in 1986 by adding the histogram component, which displays the difference between MACD and signal lines as vertical bars oscillating around zero, providing clearer visualization of momentum shifts. Traders generate signals through three primary methods: signal line crossovers where MACD crossing above signal suggests buying while crossing below indicates selling, centerline crossovers where MACD moving above zero confirms upward momentum, and divergences where price action contradicts MACD movement to forecast potential reversals. Since MACD derives from moving averages, it functions as a lagging indicator that confirms trends rather than predicting them, making it most effective in trending markets while producing whipsaws during consolidation periods.

Implementation Examples

Get MACD, Signal, and Histogram in a few lines:

use vectorta::indicators::macd::{macd, MacdInput, MacdParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with price data slice
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = MacdParams {
    fast_period: Some(12),
    slow_period: Some(26),
    signal_period: Some(9),
    ma_type: Some("ema".to_string()),
};
let input = MacdInput::from_slice(&prices, params);
let out = macd(&input)?;

// Using with Candles structure (defaults: fast=12, slow=26, signal=9; source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = MacdInput::with_default_candles(&candles);
let out = macd(&input)?;

// Access MACD components
for i in 0..out.macd.len() {
    println!("MACD: {}  Signal: {}  Hist: {}", out.macd[i], out.signal[i], out.hist[i]);
}

API Reference

Input Methods
// From price slice
MacdInput::from_slice(&[f64], MacdParams) -> MacdInput

// From candles with custom source
MacdInput::from_candles(&Candles, &str, MacdParams) -> MacdInput

// From candles with default params (close, 12/26/9)
MacdInput::with_default_candles(&Candles) -> MacdInput
Parameters Structure
pub struct MacdParams {
    pub fast_period: Option<usize>,   // Default: 12
    pub slow_period: Option<usize>,   // Default: 26
    pub signal_period: Option<usize>, // Default: 9
    pub ma_type: Option<String>,      // Default: "ema"
}
Output Structure
pub struct MacdOutput {
    pub macd: Vec<f64>,   // MACD line
    pub signal: Vec<f64>, // Signal line
    pub hist: Vec<f64>,   // Histogram = MACD - Signal
}
Validation, Warmup & NaNs
  • fast_period > 0, slow_period > 0, signal_period > 0; otherwise MacdError::InvalidPeriod.
  • There must be at least slow_period valid points after the first finite input; else MacdError::NotEnoughValidData.
  • Warmup indices are NaN: first MACD at first + slow − 1, first Signal/Hist at first + slow + signal − 2.
  • ma_type: unknown identifiers return MacdError::UnknownMA. EMA path is kernel-optimized.
Error Handling
use vectorta::indicators::macd::{macd, MacdError};

match macd(&input) {
    Ok(out) => process(out),
    Err(MacdError::AllValuesNaN) =>
        eprintln!("All input values are NaN"),
    Err(MacdError::InvalidPeriod { fast, slow, signal, data_len }) =>
        eprintln!("Invalid periods: fast={}, slow={}, signal={} (len={})", fast, slow, signal, data_len),
    Err(MacdError::NotEnoughValidData { needed, valid }) =>
        eprintln!("Need {} valid points after first finite; have {}", needed, valid),
    Err(MacdError::UnknownMA(t)) =>
        eprintln!("Unknown MA type: {}", t),
    Err(MacdError::InvalidKernel(k)) =>
        eprintln!("Invalid kernel for batch: {}", k),
}

Python Bindings

Basic Usage

Compute MACD components with NumPy arrays (EMA by default):

import numpy as np
from vectorta import macd

prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0], dtype=np.float64)

# Explicit parameters (fast=12, slow=26, signal=9, ma_type="ema")
macd_arr, signal_arr, hist_arr = macd(
    prices, 12, 26, 9, "ema", kernel="auto"
)

print(macd_arr.shape, signal_arr.shape, hist_arr.shape)
Streaming Real-time Updates

Process real-time MACD values:

from vectorta import MacdStream

stream = MacdStream(fast_period=12, slow_period=26, signal_period=9, ma_type="ema")
for price in price_feed:
    triple = stream.update(price)
    if triple is not None:
        macd_val, sig_val, hist_val = triple
        handle(macd_val, sig_val, hist_val)
Batch Parameter Optimization

Test multiple combinations and get structured outputs:

import numpy as np
from vectorta import macd_batch

prices = np.array([...], dtype=np.float64)

res = macd_batch(
    prices,
    fast_period_range=(8, 16, 4),
    slow_period_range=(20, 30, 5),
    signal_period_range=(9, 9, 0),
    ma_type="ema",
    kernel="auto"
)

# res is a dict with 2D arrays and metadata
MACD = res["macd"]      # shape: [rows, len(prices)]
SIGNAL = res["signal"]  # shape: [rows, len(prices)]
HIST = res["hist"]      # shape: [rows, len(prices)]

fasts = res["fast_periods"]
slows = res["slow_periods"]
signals = res["signal_periods"]
CUDA Acceleration

CUDA support for MACD is coming soon.

# Coming soon: CUDA-accelerated MACD APIs

JavaScript/WASM Bindings

Basic Usage

Compute MACD, Signal, and Histogram in JS/TS:

import { macd_js } from 'vectorta-wasm';

const prices = new Float64Array([100, 102, 101.5, 103, 105]);

// Returns an object with concatenated values and shape info
const res = macd_js(prices, 12, 26, 9, 'ema');
const values = res.values; // Float64Array of length 3 * prices.length
const rows = res.rows;     // 3
const cols = res.cols;     // prices.length

// Reconstruct separate series
const macd = values.slice(0, cols);
const signal = values.slice(cols, 2 * cols);
const hist = values.slice(2 * cols, 3 * cols);
Memory-Efficient Operations

Use zero-copy operations with explicit buffers:

import { macd_alloc, macd_free, macd_into, memory } from 'vectorta-wasm';

const prices = new Float64Array([/* your data */]);
const len = prices.length;

// Allocate WASM buffers
const inPtr = macd_alloc(len);
const macdPtr = macd_alloc(len);
const sigPtr = macd_alloc(len);
const histPtr = macd_alloc(len);

// Copy input data into WASM memory
new Float64Array(memory.buffer, inPtr, len).set(prices);

// Compute directly into the provided output buffers
macd_into(inPtr, macdPtr, sigPtr, histPtr, len, 12, 26, 9, 'ema');

// Read back results (slice to copy out)
const macd = new Float64Array(memory.buffer, macdPtr, len).slice();
const signal = new Float64Array(memory.buffer, sigPtr, len).slice();
const hist = new Float64Array(memory.buffer, histPtr, len).slice();

// Free allocated buffers
macd_free(inPtr, len);
macd_free(macdPtr, len);
macd_free(sigPtr, len);
macd_free(histPtr, len);
Batch Processing

Test multiple parameter combinations with metadata:

import { macd_batch_js } from 'vectorta-wasm';

const prices = new Float64Array([/* historical prices */]);

const config = {
  fast_period_range: [8, 16, 4],
  slow_period_range: [20, 30, 5],
  signal_period_range: [9, 9, 0],
  ma_type: 'ema',
};

const out = macd_batch_js(prices, config);
// out: { values: Float64Array, rows: number, cols: number, fast_periods: number[], slow_periods: number[], signal_periods: number[] }

// values layout: [macd block | signal block | hist block], each of length rows*cols
const { values, rows, cols, fast_periods, slow_periods, signal_periods } = out;

Performance Analysis

Comparison:
View:

Across sizes, Rust CPU runs about 1.43× faster than Tulip C in this benchmark.

Loading chart...

AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05

Related Indicators