Files
QuanTAlib/lib/channels/apz/apz.pine
T

71 lines
2.4 KiB
Plaintext

// Licensed under the Apache License, Version 2.0
// © 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")