Files
QuanTAlib/lib/trends_IIR/vama/vama.pine
T
2026-02-23 17:27:35 -08:00

96 lines
4.1 KiB
Plaintext

// The MIT License (MIT)
// © mihakralj
//@version=6
indicator("Volatility Adjusted Moving Average (VAMA)", "VAMA", overlay=true)
//@function Calculates VAMA by adjusting MA length based on volatility ratio using ATR
//@param source Series to calculate VAMA from
//@param base_length Base lookback period for the moving average
//@param short_atr_period Short-term ATR period for current volatility measurement
//@param long_atr_period Long-term ATR period for reference volatility measurement
//@param min_length Minimum allowed adjusted length
//@param max_length Maximum allowed adjusted length
//@returns VAMA value
//@optimized Uses RMA compensator for ATR and circular buffer for O(1) sum updates
vama(series float source, simple int base_length, simple int short_atr_period=10, simple int long_atr_period=50, simple int min_length=5, simple int max_length=100) =>
if base_length <= 0
runtime.error("Base length must be greater than 0")
if short_atr_period <= 0 or long_atr_period <= 0
runtime.error("ATR periods must be greater than 0")
if min_length <= 0 or max_length <= 0
runtime.error("Min and max length must be greater than 0")
if min_length > max_length
runtime.error("Min length must be less than or equal to max length")
var float prevClose = na
float tr1 = high - low
float tr2 = math.abs(high - prevClose)
float tr3 = math.abs(low - prevClose)
float trueRange = math.max(tr1, tr2, tr3)
prevClose := close
float EPSILON = 1e-10
var float raw_rma_short = 0.0
var float e_short = 1.0
var float raw_rma_long = 0.0
var float e_long = 1.0
float short_atr = na
float long_atr = na
if not na(trueRange)
float alpha_short = 1.0 / float(short_atr_period)
float beta_short = 1.0 - alpha_short
raw_rma_short := (raw_rma_short * (short_atr_period - 1) + trueRange) / short_atr_period
e_short := beta_short * e_short
short_atr := e_short > EPSILON ? raw_rma_short / (1.0 - e_short) : raw_rma_short
float alpha_long = 1.0 / float(long_atr_period)
float beta_long = 1.0 - alpha_long
raw_rma_long := (raw_rma_long * (long_atr_period - 1) + trueRange) / long_atr_period
e_long := beta_long * e_long
long_atr := e_long > EPSILON ? raw_rma_long / (1.0 - e_long) : raw_rma_long
float volatility_ratio = not na(short_atr) and not na(long_atr) and short_atr != 0.0 ? long_atr / short_atr : 1.0
float calc_length = base_length * volatility_ratio
int adjusted_length = int(math.max(min_length, math.min(max_length, calc_length)))
var array<float> buffer = array.new_float(max_length, na)
var int head = 0
var float sum = 0.0
var int valid_count = 0
if array.size(buffer) != max_length
buffer := array.new_float(max_length, na)
head := 0
sum := 0.0
valid_count := 0
float oldest = array.get(buffer, head)
if not na(oldest)
sum -= oldest
valid_count -= 1
if not na(source)
sum += source
valid_count += 1
array.set(buffer, head, source)
head := (head + 1) % max_length
float avg = valid_count > 0 ? sum / valid_count : source
int actual_count = math.min(valid_count, adjusted_length)
float partial_sum = 0.0
int partial_count = 0
for i = 0 to actual_count - 1
int idx = (head - 1 - i + max_length) % max_length
float val = array.get(buffer, idx)
if not na(val)
partial_sum += val
partial_count += 1
partial_count > 0 ? partial_sum / partial_count : nz(avg, source)
// ---------- Main loop ----------
// Inputs
i_base_length = input.int(20, "Base Length", minval=1)
i_source = input.source(close, "Source")
i_short_atr = input.int(10, "Short ATR Period", minval=1)
i_long_atr = input.int(50, "Long ATR Period", minval=1)
i_min_length = input.int(5, "Minimum Length", minval=1)
i_max_length = input.int(100, "Maximum Length", minval=1)
// Calculation
vama_value = vama(i_source, i_base_length, i_short_atr, i_long_atr, i_min_length, i_max_length)
// Plot
plot(vama_value, "VAMA", color=color.yellow, linewidth=2)