The Confirmed Contrarian - a Benchmark Reversion System |
Ok guys, im excited about this one. Honestly this might just be the best indicator I've created to date (might not be saying a whole lot but ya
The Confirmed Contrarian is a simple, visual trade execution framework designed to help you:
- Buy fundamentally strong stocks when they’re stretched to the downside
- Fade overheated runners when they’ve statistically overextended
- Stay out of chop when no real edge is present
It uses Z-score-based mean reversion logic, paired with RSI, relative volume, and your choice of benchmark ETF (SPY, QQQ, etc.).
The system calculates:
- Cumulative Relative Strength: Measures your stock vs an ETF (like QQQ)
- Z-Score: Tracks how far the stock has drifted from the normal relative relationship
- Rolling Bands: Based on standard deviation (like Bollinger logic but for relative performance)
- RSI: Confirms overbought/oversold context
- Relative Volume: Confirms that the market is actually stepping in
When these conditions align, you get:
- Buy arrows on strong reversion setups (Z < -1.5, RSI < 40, high relative volume)
- Sell arrows when the stock is statistically extended (Z > +1.5, RSI > 60, high volume)
- Contextual labels to give you the Z-score, RSI, volume strength, and entry confidence at a glance
| Label | Meaning |
|---|---|
| RelStrength Z | Tells you how far from "normal" the move is |
| RSI | Classic overbought/oversold zone reference |
| Volume Confirms | Confirms if action is backed by buyers/sellers |
| Buy/Sell Setup Confirmed | Only lights up when all signals align |
| Z Sentiment Zone | Interprets Z-score into plain language like: |
| “Fade or Take Profits”, “Watch for Bottoming”, “Get Ready for Bounce” | |
| Accumulation / Distribution | Shows if the market is likely rotating out or in at extremes |
- Already do research and believe in the stock
- Just want a clean, non-chasey entry system
- Prefer volume-backed mean reversion over prediction
- Want something visual, simple, and execution-focused
Check this out:
The Case Study: The highlighted bubble on the chart represents a local bottom for ENPH. If you look at the slope line on volume it shows that volume was slowing down after the sell of. Continue down to the indicator and you will see that the price was extremely down relative to the benchmark, which I chose as SMH since it is a solar ETF. You currently, as of 5/4/2025 have a similar setup on ENPH right now so keep your eyes on it and see how it responds.
Code:
declare lower;
# === Inputs ===
input benchmarkSymbol = "SPY"; # <-- type any ETF ticker from your list (e.g., "PAVE", "IVE", "GRID")
input lookback = 20;
input thresholdMult = 1.5;
input rsiLength = 14;
input rsiOversold = 40;
input rsiOverbought = 60;
input volLength = 20;
input relVolHigh = 1.5;
input relVolLow = 0.8;
input showLabels = yes;
input showArrows = yes;
input showThresholds = yes;
input useVolumeFilter = no; # if yes: require high relative volume for signals
input volumeMultiplier = 1.2; # used only when useVolumeFilter = yes
input useTrendFilter = no; # if yes: require upward trend slope for signals
input trendLength = 21;
# === Benchmark Close (NaN-safe, carry-forward) ===
def rawETF = close(symbol = benchmarkSymbol);
def etfClose =
if !IsNaN(rawETF) and rawETF != 0
then rawETF
else etfClose[1];
# === Relative Return Logic ===
def stockReturn = close / close[1] - 1;
def etfReturn = etfClose / etfClose[1] - 1;
def relReturn = stockReturn - etfReturn;
def cumStrength = Sum(relReturn, lookback);
plot CumRelStrength = cumStrength;
CumRelStrength.AssignValueColor(
if cumStrength > 0 then Color.GREEN
else if cumStrength < 0 then Color.RED
else Color.GRAY
);
CumRelStrength.SetLineWeight(2);
# === Rolling Bands (StdDev-Based) ===
def stdev = StDev(cumStrength, lookback);
plot UpperThresh = if showThresholds and !IsNaN(stdev) then thresholdMult * stdev else Double.NaN;
plot LowerThresh = if showThresholds and !IsNaN(stdev) then -thresholdMult * stdev else Double.NaN;
UpperThresh.SetDefaultColor(Color.LIGHT_GREEN);
LowerThresh.SetDefaultColor(Color.PINK);
UpperThresh.SetStyle(Curve.LONG_DASH);
LowerThresh.SetStyle(Curve.LONG_DASH);
plot ZeroLine = 0;
ZeroLine.SetDefaultColor(Color.LIGHT_GRAY);
# === Z-Score (NaN-safe) ===
def mean = Average(cumStrength, lookback);
def zScore =
if !IsNaN(stdev) and stdev != 0 and !IsNaN(mean) and !IsNaN(cumStrength)
then (cumStrength - mean) / stdev
else Double.NaN;
# === RSI and Relative Volume ===
def rsi = RSI(length = rsiLength);
def avgVol = Average(volume, volLength);
def relVol = if avgVol != 0 then volume / avgVol else 0;
# === Optional Filters ===
def volFilter =
if useVolumeFilter
then volume > avgVol * volumeMultiplier and relVol > relVolHigh
else 1;
def trend = ExpAverage(close, trendLength);
def slope = trend - trend[1];
def trendFilter =
if useTrendFilter
then slope > 0
else 1;
def showSignal = volFilter and trendFilter;
# === Reversion Buy/Sell Logic ===
# If useVolumeFilter = no, we do NOT require relVolHigh for signals.
def volConfirm = if useVolumeFilter then relVol > relVolHigh else 1;
def buySignal = !IsNaN(zScore) and zScore < -1.5 and rsi < rsiOversold and volConfirm;
def sellSignal = !IsNaN(zScore) and zScore > 1.5 and rsi > rsiOverbought and volConfirm;
# === Arrows ===
plot UpArrow = if showArrows and showSignal and buySignal then cumStrength else Double.NaN;
UpArrow.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
UpArrow.SetDefaultColor(Color.GREEN);
UpArrow.SetLineWeight(3);
plot DownArrow = if showArrows and showSignal and sellSignal then cumStrength else Double.NaN;
DownArrow.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
DownArrow.SetDefaultColor(Color.RED);
DownArrow.SetLineWeight(3);
# === Labels ===
AddLabel(showLabels,
"Benchmark: " + benchmarkSymbol,
Color.LIGHT_GRAY
);
AddLabel(showLabels,
"RelStrength Z: " + (if IsNaN(zScore) then "N/A" else AsText(Round(zScore, 2))),
if !IsNaN(zScore) and zScore < -1.5 then Color.GREEN
else if !IsNaN(zScore) and zScore > 1.5 then Color.RED
else Color.GRAY
);
AddLabel(showLabels,
"RSI: " + Round(rsi, 1),
if rsi < rsiOversold then Color.GREEN
else if rsi > rsiOverbought then Color.RED
else Color.GRAY
);
AddLabel(showLabels,
if relVol > relVolHigh then "RelVol: High"
else if relVol < relVolLow then "RelVol: Low"
else "RelVol: Neutral",
if relVol > relVolHigh then Color.GREEN
else if relVol < relVolLow then Color.ORANGE
else Color.GRAY
);
AddLabel(showLabels,
if buySignal then "Buy Setup Confirmed"
else if sellSignal then "Sell Setup Confirmed"
else "No Setup",
if buySignal then Color.CYAN
else if sellSignal then Color.PINK
else Color.DARK_GRAY
);
AddLabel(showLabels and !IsNaN(zScore),
if zScore > 2 then "Fade / Take Profits"
else if zScore > 1.5 then "Watch Buyer Exhaustion"
else if zScore > 0.5 then "Trail Stop"
else if zScore > -0.5 then "Neutral"
else if zScore > -1.5 then "Watch Bottoming"
else if zScore > -2 then "Watch Seller Exhaustion"
else "Get Ready for Bounce",
if zScore > 2 then Color.RED
else if zScore > 1.5 then Color.ORANGE
else if zScore > 0.5 then Color.YELLOW
else if zScore > -0.5 then Color.GRAY
else if zScore > -1.5 then Color.LIGHT_GREEN
else if zScore > -2 then Color.GREEN
else Color.DARK_GREEN
);
AddLabel(showLabels and !IsNaN(zScore) and (zScore > 1.5 or zScore < -1.5),
if zScore > 1.5 then "Distribution" else "Accumulation",
if zScore > 1.5 then Color.RED else Color.GREEN
);
Last edited: