# ============================================================
# AGAIG Stop & Reverse Indicator by C. Ricks 6/11/26
# Bubbles on direction change | Arrows follow trend
# Two Bubbles — white S&R below bar on bullish flip, above bar on bearish flip
# Three Labels
# S&R (n) — candle count, shifts light at 10
# Trend: LONG/SHORT | Stop: x.xx — shifts light at 10
# ATR(14): x.xx — gray, volatility reference
# ============================================================
# INPUTS
input sensitivity = 0.8; # Lower = more signals, Higher = fewer (0.1–2.0)
input atrLength = 14; # ATR period for stop distance
input atrMultiplier = 2.0; # ATR multiplier for stop distance
input showArrows = yes; # Show trend-follow arrows on every bar
input showBubbles = yes; # Show bubbles only on direction changes
input arrowOffset = 2; # Arrow distance from bar high/low (in ATR units)
input alertOnFlip = yes; # Sound alert on direction flip
input LabelSize = fontsize.medium;
input LabelLocation = location.Bottom_left;
# ============================================================
# ATR-BASED STOP CALCULATION
# ============================================================
def atr = ATR(atrLength);
def stopDist = atr * atrMultiplier * sensitivity;
# ============================================================
# STOP & REVERSE LOGIC
# Tracks a trailing stop that flips direction when price crosses it
# ============================================================
def isUpTrend;
def stopLevel;
# Seed: use close vs prior close for initial direction
def initUp = close > close[1];
# Trailing stop logic — flips when price crosses the stop
isUpTrend = if IsNaN(isUpTrend[1])
then initUp
else if isUpTrend[1] and low < stopLevel[1]
then 0 # Flip to downtrend
else if !isUpTrend[1] and high > stopLevel[1]
then 1 # Flip to uptrend
else isUpTrend[1];
stopLevel = if IsNaN(stopLevel[1])
then if isUpTrend then low - stopDist else high + stopDist
else if isUpTrend and isUpTrend[1]
then Max(stopLevel[1], low - stopDist) # Trail stop up in uptrend
else if !isUpTrend and !isUpTrend[1]
then Min(stopLevel[1], high + stopDist) # Trail stop down in downtrend
else if isUpTrend and !isUpTrend[1]
then low - stopDist # Reset on flip to uptrend
else high + stopDist; # Reset on flip to downtrend
# ============================================================
# DIRECTION CHANGE DETECTION
# ============================================================
def flippedUp = isUpTrend and !isUpTrend[1]; # Just turned bullish
def flippedDown = !isUpTrend and isUpTrend[1]; # Just turned bearish
# ============================================================
# CANDLE COUNT — bars elapsed since last direction change
# ============================================================
def candleCount;
candleCount = if flippedUp or flippedDown
then 1
else candleCount[1] + 1;
# ============================================================
# HEIKIN ASHI PRICE REFERENCES
# ============================================================
def haHigh = Fundamental(FundamentalType.HIGH, period = GetAggregationPeriod());
def haLow = Fundamental(FundamentalType.LOW, period = GetAggregationPeriod());
def haMid = (haHigh + haLow) / 2;
def arrowPad = atr * arrowOffset * 0.15;
# ============================================================
# EMBEDDED DIRECTIONAL ARROWS — Green up / Red down, size 1
# ============================================================
input markerSize = 2;
input arrowCutoff = 6; # Stop showing trend arrows after this many candles
plot BullMarker = if showArrows and isUpTrend and candleCount <= arrowCutoff then haMid else Double.NaN;
BullMarker.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
BullMarker.SetDefaultColor(Color.GREEN);
BullMarker.SetLineWeight(markerSize);
plot BearMarker = if showArrows and !isUpTrend and candleCount <= arrowCutoff then haMid else Double.NaN;
BearMarker.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
BearMarker.SetDefaultColor(Color.RED);
BearMarker.SetLineWeight(markerSize);
# ============================================================
# DIRECTION CHANGE BUBBLES — white background
# ============================================================
AddChartBubble(
showBubbles and flippedUp,
haLow - arrowPad * 2,
"S&R Long",
Color.WHITE,
no
);
AddChartBubble(
showBubbles and flippedDown,
haHigh + arrowPad * 2,
"S&R Short",
Color.WHITE,
yes
);
# ============================================================
# CANDLE COLORING (optional paintbar)
# ============================================================
AssignPriceColor(
if isUpTrend
then Color.GREEN
else Color.RED
);
# ============================================================
# ALERTS
# ============================================================
Alert(alertOnFlip and flippedUp, "Trend Spark SAR — Flipped BULLISH", Alert.BAR, Sound.Chimes);
Alert(alertOnFlip and flippedDown, "Trend Spark SAR — Flipped BEARISH", Alert.BAR, Sound.Bell);
# ============================================================
# EXTENDED TREND THRESHOLD
# ============================================================
input extendedThreshold = 10; # Candles before trend is considered extended
def isExtended = candleCount >= extendedThreshold;
# Color logic: bright green/red = active, light = extended
# ============================================================
# LABELS (status bar)
# S&R label with candle count | Trend & Stop | ATR
# ============================================================
AddLabel(yes,
"S & R(" + candleCount + ")",
if isUpTrend
then (if isExtended then Color.LIGHT_GREEN else Color.GREEN)
else (if isExtended then Color.PINK else Color.RED), LabelLocation, LabelSize
);
AddLabel(yes,
if isUpTrend
then "Trend: LONG | Stop: " + Round(stopLevel, 2)
else "Trend: SHORT | Stop: " + Round(stopLevel, 2),
if isUpTrend
then (if isExtended then Color.LIGHT_GREEN else Color.GREEN)
else (if isExtended then Color.PINK else Color.RED), LabelLocation, LabelSize
);
AddLabel(yes,
"ATR(" + atrLength + "): " + Round(atr, 2),
Color.GRAY, LabelLocation, LabelSize
);