trading2026-08-10Β·8 minΒ·35/68

MACD Divergence Backtest on BTC/USDT: -17.45% Net β€” the Divergence Family Goes 0-for-2

MACD bullish divergence on real hourly BTC/USDT 2023: -6.88% naive, -17.45% after fees and funding, 91 trades. The divergence family (RSI post 09, MACD here) is now the lab's least reliable pattern.

MACD Divergence Backtest on BTC/USDT (2023, hourly, real fees)

Post 09 tested RSI bullish divergence β€” price makes a lower low, the oscillator makes a higher low β€” and it limped to +6.62% net. This post tests the identical pattern on the MACD line: a lower price low with a higher MACD low, held for a fixed window after confirmation. It is the same idea, a different oscillator, and it is the first strategy in the lab to lose money before costs. Divergence is now 0-for-2. Strategy Lab #63.

The setup

Compare the price low of the last 20 bars to the price low 20 bars before it, and the same for MACD. When price lows are lower but MACD lows are higher (bullish divergence), go long and hold for 24 bars. Same engine, same costs.

Results

StrategyCategoryTotal returnCAGRMaxDDSharpeTrades
macd_divmomentum-17.45%-17.49%-23.55%-0.9091

MACD divergence vs buy & hold (2023, hourly, BTC/USDT)

The fee bill

| Scenario | Cost/leg | Total return | MaxDD | Sharpe | Trades | |---|---:|---:|---:|---:|---:|---:| | naive (zero cost) | 0.00% | -6.88% | -17.05% | -0.28 | 91 | | taker fee 0.05%/leg | 0.05% | -14.98% | -21.86% | -0.75 | 91 | | + funding 0.01%/8h | 0.05% | -17.45% | -23.55% | -0.90 | 91 | | + slippage 10bp/leg | 0.15% | -31.20% | -33.72% | -1.85 | 91 | | + slippage 25bp/leg | 0.30% | -47.67% | -48.15% | -3.22 | 91 |

The naive column is the headline: -6.88% before any cost at all. Every other strategy in the lab β€” even the volume family's disasters β€” was net-positive gross. MACD divergence loses even if the exchange is free. 91 trades is a moderate count, so this is not a frequency death; the signal itself is selecting the wrong moments. On a year that mostly trended up, the pattern is "buy the second lower low hoping for a turn" β€” and 2023 BTC kept making lower lows in the spring correction and the autumn range, then turned up only after the pattern had already been hit multiple times.

The divergence family, both members

| Pattern | Post | Trades | Naive | Fees+funding | |---|---:|---:|---:|---:|---:| | MACD bullish div (this) | β€” | 91 | -6.88% | -17.45% | | RSI bullish div | 09 | 90 | +19.96% | +6.62% |

Two divergences, two nearly identical trade counts (91 vs 90), opposite signs gross. The RSI version at least cleared costs; the MACD version did not. Neither comes close to the signal's reputation β€” divergence is the single most-cited "turning point" pattern in retail trading, and the lab has now tested two implementations of it against real hourly data and neither produced an edge worth paying for. The honest summary: divergence is a description of exhaustion, not a timing signal, and a divergence confirmation without a trend filter is a coin flip that pays the spread.

What this does NOT prove

  • The 20-bar window and 24-bar hold are the same as post 09's; a longer confirmation window or a regime filter (e.g. only trade divergences against the prevailing EMA) could change the result β€” that is a real strategy, untested here.
  • Divergence on higher timeframes (daily) trades fewer times and might follow the lab's frequency pattern, but that is speculation.
  • One pair, one year, one regime.

Code

from backtest_base import fetch, backtest_signal, metrics
from strategy import sig_macd_div

df = fetch("BTCUSDT", "binance", "2023-01-01", "2023-12-31", "1h")
signal = sig_macd_div(df, fast=12, slow=26, signal=9, window=20, hold=24)

res = backtest_signal(df, signal, cost_per_leg=0.0005, funding_per_bar=0.0000125)
print(metrics(res, 8760))

Reproduce it

cd blog-drafts/scripts
python gen_post_assets.py --ids 63

Data: Binance public API, hourly OHLCV, 8,735 bars. Tables above reproduce exactly from this command.

This is a backtest on historical data, not investment advice. Past performance does not predict future results.