MACD Histogram
Momentum from EMA difference & its signal line.
MACD asks: is momentum waking up or running out of steam? It looks at the difference between two trend-followers. When that difference flips from negative to positive, momentum just turned on — buy. When it flips negative, it's running out — sell.
MACD looks at the difference between two EMA-based trend-followers. When that difference flips from negative to positive, momentum just woke up. When it flips negative, it's running out of steam. The histogram is the difference between the MACD line and its own signal line — that's where the actual cross signals come from.
MACD = EMA(fast) − EMA(slow); signal = EMA(MACD)
Real MACD Histogram bot, running on real Yahoo data when the symbol is available. Drag the params — the bot re-runs instantly.
This is the actual code the bot runs — not a re-explanation, not a simplified version. Whatever ships here is what executes when you press Run All in the workbench. Read it, copy it, fork it, build a better one.
const macdBot: BotDef = {
id: "macd",
name: "MACD Histogram",
category: "trend",
glyph: "▥",
tagline: "Momentum from EMA difference & its signal line.",
formula: "MACD = EMA(fast) − EMA(slow); signal = EMA(MACD)",
params: [
{ key: "fast", label: "Fast EMA", kind: "number", default: 12, min: 5, max: 26, step: 1 },
{ key: "slow", label: "Slow EMA", kind: "number", default: 26, min: 17, max: 52, step: 1 },
{ key: "signal", label: "Signal EMA", kind: "number", default: 9, min: 3, max: 18, step: 1 },
],
run: (ctx, p): BotResult => {
const fast = Math.round(num(p, "fast", 12));
const slow = Math.round(num(p, "slow", 26));
const sig = Math.round(num(p, "signal", 9));
const px = closes(ctx.candles);
const m = macd(px, fast, slow, sig);
const signals: Signal[] = [];
const sigArr: (1 | -1 | 0)[] = px.map(() => 0);
for (let i = 1; i < m.histogram.length; i++) {
const a = m.histogram[i - 1], b = m.histogram[i];
if (a == null || b == null) continue;
if (a <= 0 && b > 0) {
signals.push({ i, kind: "buy", price: px[i], label: "hist > 0" });
sigArr[i] = 1;
} else if (a >= 0 && b < 0) {
signals.push({ i, kind: "sell", price: px[i], label: "hist < 0" });
sigArr[i] = -1;
}
}
const bt = backtestLongOnly(px, sigArr);
const lastH = m.histogram[m.histogram.length - 1] ?? 0;
return {
signals,
metrics: [
{ key: "hist", label: "Hist", value: fmtNum(lastH, 3), tone: lastH > 0 ? "bull" : "bear" },
{ key: "line", label: "MACD", value: fmtNum(m.line[m.line.length - 1] ?? 0, 3), tone: "neutral" },
{ key: "trades", label: "Trades", value: String(bt.trades), tone: "neutral" },
{ key: "win", label: "Win rate", value: `${(bt.winRate * 100).toFixed(0)}%`, tone: bt.winRate > 0.5 ? "bull" : "warn" },
],
pane: {
kind: "histogram",
series: [
{ values: m.histogram, color: "var(--plasma)", label: "Hist" },
{ values: m.line, color: "var(--bull)", label: "MACD" },
{ values: m.signal, color: "var(--cyan)", label: "Signal" },
],
refLines: [{ value: 0, color: "var(--fg-faint)" }],
height: 90,
},
summary: `MACD histogram ${lastH.toFixed(3)}. ${signals.length} crosses, ${bt.trades} trades, win ${(bt.winRate * 100).toFixed(0)}%.`,
beginner:
"MACD looks at the difference between two trend-followers. When that difference flips from negative to positive, momentum just woke up. When it flips negative, it's running out of steam.",
verdict: {
side: lastH > 0 ? "buy" : "sell",
text: lastH > 0 ? "Histogram above zero — momentum tilts long." : "Histogram below zero — momentum tilts short.",
confidence: Math.min(1, Math.abs(lastH) * 5),
},
equity: bt.equity,
};
},
};id— unique key the workbench uses to find the bot.params— the sliders + inputs you see on the cell.run(ctx, p)— the function that gets called with candles + your params and returns the verdict.verdict— the BUY / SELL / HOLD pill at the top of the cell.metrics— the small stat boxes shown in the cell body.
- Copy the whole block above.
- On /quant, click + Import your bot in the bot library.
- Paste, hit save. It hot-loads into your workspace.
- Edit any param defaults or logic to your taste — it's now yours.
- ·Pulse-checking momentum. Histogram crossing zero = momentum just turned, regardless of trend direction.
- ·Catching divergences. Price making higher highs while MACD makes lower highs is one of the strongest reversal signals in technical analysis.
- ·Daily and 4h timeframes on liquid names. The default 12/26/9 was tuned for daily.
- ·Choppy ranges. Histogram crosses zero every other bar; whipsaws kill returns.
- ·Slow-drifting markets. MACD can sit near zero for weeks waiting for momentum.
- ·Short-window instruments (intraday small caps). Default periods are too slow.
BUY when histogram crosses above zero. SELL when below. Confidence scales with the magnitude of the histogram. Above-zero histogram with rising MACD line is the strongest BUY shape; mirror for SELL.
Why not just use the line/signal cross?+
12/26/9 — should I tune these?+
Buy when fast moving average crosses above slow.
Catch oversold bounces, fade overbought spikes.
Volatility envelopes around a moving average.
The Turtle trade — buy n-day highs, sell n-day lows.