MACD#
Description#
MACD (Moving Average Convergence Divergence, Gerald Appel) is a momentum indicator that subtracts a slow EMA from a fast EMA and then smooths the result. It returns three quantities at every step:
MACD is a 1 → 3 functor: pass one input stream, get back the triple (macd, signal, histogram) per step.
Parameters#
fast(int, default12): span of the fast EMA.slow(int, default26): span of the slow EMA. Must be strictly greater thanfast.signal(int, default9): span of the signal-line EMA.
Defaults match Appel's original choice and every charting platform's default.
Warmup: none. Each underlying EMA is screamer.EwMean, which is well-defined from sample t=0 (EMA[0] = x[0]). At t=0 both EMAs equal x[0], so macd[0] = 0, signal[0] = 0, histogram[0] = 0. From t=1 onward all three outputs are non-trivial.
NaN handling: NaN inputs propagate through the EMA arithmetic and poison subsequent outputs.
Output shape#
You pass... |
You get back... |
|---|---|
scalar |
|
1D array shape |
array shape |
2D array shape |
array shape |
out[..., 0] is macd, out[..., 1] is signal, out[..., 2] is histogram.
Implementation Details#
Pure composition of three EwMean instances. Per step:
Time complexity:
O(1)per step.Space complexity:
O(1)(eachEwMeanholds two scalars).
NaN handling#
Policy: ignore. A NaN in any input at index t causes the function to skip that step: output at t is NaN and internal state is unchanged. Subsequent finite samples are processed as if step t had not occurred.
Examples#
Implementation Details#
f = ema_fast.process_scalar(x) // span = fast
s = ema_slow.process_scalar(x) // span = slow
macd = f - s
signal = ema_signal.process_scalar(macd) // span = signal
histogram = macd - signal
Usage example#
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from screamer import MACD
rng = np.random.default_rng(0)
n = 300
price = 100 + np.cumsum(rng.normal(0.0, 1.0, n))
out = MACD()(price)
macd, signal, hist = out[:, 0], out[:, 1], out[:, 2]
fig = make_subplots(rows=2, cols=1, shared_xaxes=True,
row_heights=[0.55, 0.45], vertical_spacing=0.08)
fig.add_trace(go.Scatter(y=price, mode='lines', name='Price',
line=dict(color='steelblue')),
row=1, col=1)
fig.add_trace(go.Scatter(y=macd, mode='lines', name='MACD',
line=dict(color='steelblue')),
row=2, col=1)
fig.add_trace(go.Scatter(y=signal, mode='lines', name='Signal',
line=dict(color='red')),
row=2, col=1)
fig.add_trace(go.Bar(y=hist, name='Histogram',
marker=dict(color='gray')),
row=2, col=1)
fig.update_layout(
title="MACD(12, 26, 9): fast/slow EMA difference plus signal smoothing",
xaxis_title="Index",
yaxis_title="Price",
yaxis2_title="MACD components",
margin=dict(l=20, r=20, t=60, b=20),
legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
)
fig.show()
Reference#
Matches pandas.Series.ewm(span=..., adjust=True).mean() composition exactly (verified in tests/test_macd.py). The underlying EMA is the bias-corrected weighted mean -- the statistically clean form used throughout screamer. TA-Lib's MACD uses a different EMA convention (adjust=False with an SMA-seeded warmup) and therefore differs from our output by a few percent during early samples; see conventions for the comparison matrix.