MACD
Read the Spreads Before You Read the Signals
Install and import#
npm install fintech-algorithmsimport { macd } from "fintech-algorithms/technical-indicators/trend-systems/macd";Signature#
macd(values, fastSpan, slowSpan, signalSpan)Moving average convergence/divergence: the gap between a fast and a slow EMA, its own EMA as a signal line, and the difference between them as a histogram.
Parameters#
| Name | Type | Notes |
|---|---|---|
values | (number | null)[] | Observation series in chronological order, oldest first. nulls: propagate |
fastSpan | number | Span of the fast EMA. min: 1 · integer: true |
slowSpan | number | Span of the slow EMA; must exceed the fast span. min: 1 · integer: true |
signalSpan | number | Span of the EMA taken over the MACD line itself. min: 1 · integer: true |
Returns#
{ fast_ema, slow_ema, macd, signal, histogram }[] · length same-as-input
One record per position carrying both EMAs alongside the three published series.
Warm-up#
The first slowSpan − 1 for the MACD line, and slowSpan + signalSpan − 2 for the signal and histogram positions are null. The signal line is an average *of* the MACD line, so it is defined strictly later — plotting the two without allowing for that misaligns them.
Errors#
- When any span is < 1, is not an integer, or fastSpan ≥ slowSpan — throws RangeError
Complexity: time O(n),
space O(n).
Worked example#
verified This is the worked example published in the article, replayed by the test suite on every run. The output cannot drift.
Input#
[10, 11, 12, 13, 14, 15]Showing 6 of 14 elements.
353Call#
macd(values, fastSpan, slowSpan, signalSpan)Returns#
object with 5 fields: 4, 6, 7, 9, 12
{
"4": {
"fastEma": 13,
"slowEma": 12,
"macd": 1,
"signal": null,
"histogram": null,
"status": "warming_signal"
},
"6": {
"fastEma": 15,
"slowEma": 14,
"macd": 1,
"signal": 1,
"histogram": 0,
"status": "ready"
},
"7": {
"fastEma": 15,
"slowEma": 14.333333333333334,
"macd": 0.6666666666666661,
"signal": 0.833333333333333,
"histogram": -0.16666666666666696,
"status": "ready"
},
"9": {
"macd": -0.06481481481481488,
"signal": 0.24537037037037002,
"histogram": -0.3101851851851849,
"status": "ready"
},
"12": {
"macd": 0.2088048696844993,
"signal": 0.04260973936899856,
"histogram": 0.16619513031550073,
"status": "ready"
}
}Diagrams#
Calculation flow#
Calculation flow
flowchart LR
X["Ordered finite source values"] --> F["Fast SMA-seeded EMA"]
X --> S["Slow SMA-seeded EMA"]
F --> D{"Both EMAs ready?"}
S --> D
D -->|No| W["MACD unavailable"]
D -->|Yes| M["MACD = fast EMA - slow EMA"]
M --> G["Signal = SMA-seeded EMA of MACD"]
M --> H["Histogram = MACD - signal"]
G --> H
H --> O["Aligned component row"]
State lifecycle
stateDiagram-v2
[*] --> WarmingPriceEMAs
WarmingPriceEMAs --> WarmingSignal: "slow_span observations"
WarmingSignal --> Ready: "signal_span MACD values"
Ready --> Ready: "append one finalized observation"
Ready --> RecomputedSuffix: "revise a historical observation"
RecomputedSuffix --> Ready: "replace affected suffix"
How it works#
This page states the contract — how to call it correctly. The article explains the concept: why it works, and where it breaks.
References#
- Primary and authoritative sources
- Claims derived algebraically in this package
- Historical and interpretive caution
- Reproducibility
- Historical example decision