Code: Select all
// This work is licensed under an Attribution-ShareAlike 4.0 International License (CC BY-SA 4.0)
// https://creativecommons.org/licenses/by-sa/4.0/
// © Uptrick
//@version=6
// ██╗ ██╗██████╗ ████████╗██████╗ ██╗ ██████╗██╗ ██╗
// ██║ ██║██╔══██╗╚══██╔══╝██╔══██╗██║██╔════╝██║ ██╔╝
// ██║ ██║██████╔╝ ██║ ██████╔╝██║██║ █████╔╝
// ██║ ██║██╔═══╝ ██║ ██╔══██╗██║██║ ██╔═██╗
// ╚██████╔╝██║ ██║ ██║ ██║██║╚██████╗██║ ██╗
// ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═════╝╚═╝ ╚═╝
indicator("Uptrick: Volatility Adjusted Trail", overlay=true)
//== Inputs
emaLen = input.int(8, "Basis EMA Length", minval=2)
atrLen = input.int(14, "ATR Length", minval=1)
baseMult = input.float(2.0, "Base ATR Mult", minval=0.1, step=0.1)
sensExp = input.float(1.00, "Volatility Expansion Sensitivity", minval=0.10, step=0.05)
persLen = input.int(20, "Trend Persistence Window", minval=2)
persGain = input.float(0.30, "Persistence Impact (0..1)", minval=0.0, maxval=1.0, step=0.05)
multMin = input.float(1.0, "Min Effective Mult", minval=0.1, step=0.1)
multMax = input.float(4.0, "Max Effective Mult", minval=0.5, step=0.1)
confirmN = input.int(1, "Bars Above/Below to Confirm Flip", minval=1)
//== Colors (Uptrick palette)
bullCol = color.rgb(92,240,215) // #5CF0D7
bearCol = color.rgb(179,42,195) // #B32AC3
midGray = color.new(color.gray, 100)
fillBull = color.new(color.rgb(88,237,212), 80)
fillBear = color.new(color.rgb(177,41,192), 80)
//== Core series
basis = ta.ema(close, emaLen)
atr = ta.atr(atrLen)
atrMA = ta.sma(atr, atrLen)
expRatio = atrMA == 0.0 ? 1.0 : atr / atrMA // >1 = expansion, <1 = compression
expAdj = math.pow(expRatio, sensExp) // sensitivity to expansion
slope = basis - basis[1]
dirStep = slope >= 0 ? 1.0 : -1.0
persist = ta.ema(dirStep, persLen) // -1..+1 trend memory
persistAdj = 1.0 + persGain * math.abs(persist) // widen when trend is consistent
dynMultRaw = baseMult * expAdj * persistAdj
dynMult = math.min(math.max(dynMultRaw, multMin), multMax) // clamp to bounds
upperBand = basis + dynMult * atr
lowerBand = basis - dynMult * atr
//== Trailing bands (Supertrend-style)
var float trailUp = na
var float trailDn = na
var int state = 0 // 1 bull, -1 bear, 0 neutral
if na(trailUp)
trailUp := lowerBand
if na(trailDn)
trailDn := upperBand
if state == 1
trailUp := math.max(lowerBand, nz(trailUp[1]))
trailDn := upperBand // reset opposing side
if state == -1
trailDn := math.min(upperBand, nz(trailDn[1]))
trailUp := lowerBand
//== Flip conditions with persistence confirmation
var int upCnt = 0
var int dnCnt = 0
aboveDn = close > trailDn
belowUp = close < trailUp
if aboveDn
upCnt += 1
else
upCnt := 0
if belowUp
dnCnt += 1
else
dnCnt := 0
if state == 0
if upCnt >= confirmN
state := 1
if dnCnt >= confirmN
state := -1
else
if state == 1 and dnCnt >= confirmN
state := -1
if state == -1 and upCnt >= confirmN
state := 1
//== Visuals
isBull = state == 1
isBear = state == -1
ribColor = isBull ? bullCol : isBear ? bearCol : midGray
pUpper = plot(trailDn, "ASB Upper Trail", color=isBear ? bearCol : midGray, linewidth=2, style=plot.style_linebr)
pLower = plot(trailUp, "ASB Lower Trail", color=isBull ? bullCol : midGray, linewidth=2, style=plot.style_linebr)
price = plot(close, color=midGray)
fill(pUpper, price, trailDn, close, isBear? color.new(bearCol,50): midGray, midGray,'FILL COLOR')
fill(pLower, price, close, trailUp, midGray, isBull? color.new(bullCol,50): midGray,'FILL COLOR')
barcolor(isBull ? bullCol : isBear ? bearCol : color.new(color.gray, 70))
//== Flip markers & alerts
bullFlip = ta.change(state) == 2 // -1 -> 1
bearFlip = ta.change(state) == -2 // 1 -> -1
// === Label settings
showLabels = input.bool(true, "Show Flip Labels")
lblSize = input.string("Normal", "Label Size", options=["Tiny","Small","Normal","Large","Huge"])
lblAtrOffset = input.float(0.60, "Label ATR Offset (× ATR)")
toSize(s) =>
s == "Tiny" ? size.tiny :
s == "Small" ? size.small :
s == "Normal" ? size.normal:
s == "Large" ? size.large :
size.huge
// === Flip labels anchored to bands
if showLabels and bullFlip
yDn = trailUp - lblAtrOffset * atr // below lower band
label.new(bar_index, yDn,
text="𝓤𝓹",
style=label.style_label_up,
size=toSize(lblSize),
color=color.new(bullCol, 0),
textcolor=#000000)
if showLabels and bearFlip
yUp = trailDn + lblAtrOffset * atr // above upper band
label.new(bar_index, yUp,
text="𝓓𝓸𝔀𝓷",
style=label.style_label_down,
size=toSize(lblSize),
color=color.new(bearCol, 0),
textcolor=#ffffff)
alertcondition(bullFlip, "ASB Bull Flip", "ASB: Bullish flip on {{ticker}} {{interval}}")
alertcondition(bearFlip, "ASB Bear Flip", "ASB: Bearish flip on {{ticker}} {{interval}}")
plotcandle(open,high,low,close,'Plotcandle',isBull ? bullCol : isBear ? bearCol : color.new(color.gray, 70),isBull ? bullCol : isBear ? bearCol : color.new(color.gray, 70),bordercolor = isBull ? bullCol : isBear ? bearCol : color.new(color.gray, 70),force_overlay = true,display = display.pane)