// The MIT License (MIT) // © mihakralj //@version=6 indicator("Adaptive Price Zone", "APZ", overlay=true) //@function Calculates Adaptive Price Zone using double-smoothed EMA //@param source Series to calculate middle line from //@param period Lookback period (sqrt applied internally for smoothing) //@param bandPct Band width multiplier //@returns tuple with [middle, upper, lower] band values //@optimized Uses compound warmup compensation for nested EMAs, O(1) complexity per bar apz(series float source, simple int period, simple float bandPct) => if period <= 0 runtime.error("Period must be greater than 0") if period > 5000 runtime.error("Period exceeds maximum of 5000") if bandPct <= 0.0 runtime.error("Band multiplier must be greater than 0") float smoothPeriod = math.sqrt(period) float alpha = 2.0 / (smoothPeriod + 1.0) float beta = 1.0 - alpha var float ema1_price = 0.0 var float ema2_price = 0.0 var float ema1_range = 0.0 var float ema2_range = 0.0 var float e = 1.0 var bool warmup = true float current_price = nz(source) float current_range = nz(high - low) ema1_price := alpha * current_price + beta * ema1_price ema2_price := alpha * ema1_price + beta * ema2_price ema1_range := alpha * current_range + beta * ema1_range ema2_range := alpha * ema1_range + beta * ema2_range float middle = ema2_price float adaptiveRange = ema2_range if warmup e *= beta * beta float compensator = 1.0 / (1.0 - e) middle := compensator * ema2_price adaptiveRange := compensator * ema2_range warmup := e > 1e-10 float width = bandPct * adaptiveRange float upper = middle + width float lower = middle - width [middle, upper, lower] // ---------- Main loop ---------- // Inputs i_period = input.int(20, "Period", minval=1, maxval=5000) i_bandPct = input.float(2.0, "Band Multiplier", minval=0.001, step=0.1) i_source = input.source(close, "Source") // Calculation [middle, upper, lower] = apz(i_source, i_period, i_bandPct) // Plot plot(middle, "Middle", color=color.yellow, linewidth=2) p1 = plot(upper, "Upper", color=color.new(color.yellow, 50), linewidth=1) p2 = plot(lower, "Lower", color=color.new(color.yellow, 50), linewidth=1) fill(p1, p2, color=color.new(color.yellow, 90), title="Band Fill")