fintech-algorithms
Using a coding agent? Give it the skill: npx skills add IslamBaraka90/Fintech-Algorithms-Library What it does →

MACD

Read the Spreads Before You Read the Signals

Install and import#

bash
npm install fintech-algorithms
ts
import { 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#

NameTypeNotes
values(number | null)[]Observation series in chronological order, oldest first.
nulls: propagate
fastSpannumberSpan of the fast EMA.
min: 1 · integer: true
slowSpannumberSpan of the slow EMA; must exceed the fast span.
min: 1 · integer: true
signalSpannumberSpan 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#

values
[10, 11, 12, 13, 14, 15]

Showing 6 of 14 elements.

fastSpan
3
slowSpan
5
signalSpan
3

Call#

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#

MACD — ema gap
MACD — macd machine
MACD — scale trap
MACD — signal cross
MACD — three panel path
MACD — warmup timeline
MACD — whipsaw range
MACD — zero cross

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.

Read the article →

References#

  • Primary and authoritative sources
  • Claims derived algebraically in this package
  • Historical and interpretive caution
  • Reproducibility
  • Historical example decision

The rest of the Trend Systems family#