// Licensed under the Apache License, Version 2.0 // © mihakralj //@version=6 indicator("Adaptive Price Channel (APCHANNEL)", "APCHANNEL", overlay=true) //@function Calculates the Adaptive Price Channel using dual EMA on highs and lows. //@doc The channel applies exponential smoothing to price highs and lows independently, // creating a dynamic envelope that "remembers" significant extremes while gradually // fading their influence. Unlike fixed-window Donchian channels that drop extremes // abruptly, APCHANNEL decays them smoothly (leaky integration). //@param alpha Smoothing factor (0 < alpha <= 1). Higher = faster decay, shorter memory. //@returns tuple of [middle, upper, lower] band values //@optimized O(1) per bar via EMA recursion; uses FMA pattern: decay*prev + alpha*new apchannel(simple float alpha) => if alpha <= 0.0 or alpha > 1.0 runtime.error("Alpha must be > 0 and <= 1") [float(na), float(na), float(na)] float decay = 1.0 - alpha // EMA of highs (upper band) var float high_ema = na float valid_high = nz(high, nz(high_ema, 0.0)) if na(high_ema) high_ema := valid_high else high_ema := decay * high_ema + alpha * valid_high // EMA of lows (lower band) var float low_ema = na float valid_low = nz(low, nz(low_ema, 0.0)) if na(low_ema) low_ema := valid_low else low_ema := decay * low_ema + alpha * valid_low // Midpoint float mid = (high_ema + low_ema) / 2.0 [mid, high_ema, low_ema] // ---------- Main loop ---------- // Inputs i_alpha = input.float(0.2, "Alpha (smoothing factor)", minval=0.01, maxval=1.0, step=0.01) // Calculation [middle, upper, lower] = apchannel(i_alpha) // Plot plot(middle, "Middle", color=color.yellow, linewidth=2) p1 = plot(upper, "Upper", color=color.new(color.blue, 50), linewidth=1) p2 = plot(lower, "Lower", color=color.new(color.blue, 50), linewidth=1) fill(p1, p2, color=color.new(color.blue, 90))