mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-13 08:08:05 +00:00
96 lines
4.1 KiB
Plaintext
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)
|