Profitable Pair Correlation Divergence Scanner v6

Published: (December 8, 2025 at 06:48 AM EST)
2 min read
Source: Dev.to

Source: Dev.to

Overview

This strategy identifies divergence opportunities between two correlated assets using a combination of Z‑Score spread analysis, trend confirmation, RSI & MACD momentum checks, correlation filters, and ATR‑based stop‑loss/take‑profit management. It’s optimized for positive P&L and realistic trade execution.

Key Features

  • Pair Divergence Detection – Measures the spread between the returns of two assets and normalizes it with a Z‑Score.
  • Trend Alignment – Uses fast and slow EMAs to ensure trades are taken in the direction of the prevailing trend.
  • Momentum Confirmation – RSI and MACD filters confirm bullish or bearish momentum.
  • Correlation Filter – Requires a minimum correlation between the pair (default > 0.5) to reduce false signals.
  • Risk Management – ATR‑based stop‑loss and take‑profit levels, with a reward‑to‑risk ratio greater than 1.
  • Exit Conditions – Positions are closed when the Z‑Score re‑enters a tight “normalization” zone.

How It Works

Calculate Returns

ret1 = ta.roc(s1, 1)
ret2 = ta.roc(s2, 1)

Z‑Score Spread

spread   = ret1 - ret2
spreadMA = ta.sma(spread, zLen)
spreadSD = ta.stdev(spread, zLen)
zScore   = (spread - spreadMA) / spreadSD

Trend Filter

emaFast = ta.ema(s1, fastEMA)
emaSlow = ta.ema(s1, slowEMA)
trendLong  = emaFast > emaSlow
trendShort = emaFast  50
rsiShort = rsiVal  signalLine
macdShort = macdLine  0.5

Trade Execution

longCond  = zScore   entryZ and trendShort and rsiShort and macdShort and corrFilter

if (longCond)
    strategy.entry("LongSpread", strategy.long)
if (shortCond)
    strategy.entry("ShortSpread", strategy.short)

Exit Logic

exitLong  = math.abs(zScore)  emaSlow
trendShort = emaFast  50
rsiShort = rsiVal  signalLine
macdShort = macdLine  0.5

// ATR FOR SL/TP
atrVal = ta.atr(atrLen)
sl = atrVal * atrMult
tp = atrVal * atrMult * 2  // reward > risk

// ENTRY CONDITIONS
longCond  = zScore   entryZ and trendShort and rsiShort and macdShort and corrFilter

exitLong  = math.abs(zScore) < exitZ
exitShort = math.abs(zScore) < exitZ

// EXECUTION
if (longCond)
    strategy.entry("LongSpread", strategy.long)
if (shortCond)
    strategy.entry("ShortSpread", strategy.short)
if (exitLong)
    strategy.close("LongSpread")
if (exitShort)
    strategy.close("ShortSpread")

// ATR‑BASED EXIT
strategy.exit("Exit Long",  "LongSpread", stop=close - sl, limit=close + tp)
strategy.exit("Exit Short", "ShortSpread", stop=close + sl, limit=close - tp)

// PLOTTING
plot(zScore, "Z-Score", color=color.new(color.blue, 0))
hline( entryZ,  "Upper Entry", color=color.red)
hline(-entryZ, "Lower Entry", color=color.green)
hline( exitZ,  "Exit Zone",   color=color.gray)
hline(-exitZ,  "Exit Zone",   color=color.gray)
plot(emaFast, "Fast EMA", color=color.new(color.purple, 0))
plot(emaSlow, "Slow EMA", color=color.new(color.orange, 0))
plot(corrVal, "Correlation", color=color.new(color.teal, 40))
Back to Blog

Related posts

Read more »