Files

51 lines
1.9 KiB
Plaintext

// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Arnaud Legoux Moving Average (ALMA)", "ALMA", overlay=true)
//@function Calculates ALMA using Gaussian distribution weights
//@param source Series to calculate ALMA from
//@param period Lookback period - window size
//@param offset Controls the Gaussian peak location (0 to 1)
//@param sigma Controls the Gaussian distribution width/curve shape
//@returns ALMA value, calculates from first bar using available data
//@optimized Uses Gaussian weighting with O(n) complexity per bar due to lookback loop
alma(series float source, simple int period, simple float offset=0.85, simple float sigma=6.0) =>
if period <= 0
runtime.error("Period must be greater than 0")
if offset < 0.0 or offset > 1.0
runtime.error("Offset must be between 0 and 1")
if sigma <= 0.0
runtime.error("Sigma must be greater than 0")
int p = math.min(bar_index + 1, period)
if p <= 1
source
else
float m = (1.0 - offset) * (p - 1)
float s = p / sigma
float s2 = 2.0 * (s * s)
float sum = 0.0
float weight_sum = 0.0
for i = 0 to p - 1
float price = source[i]
if not na(price)
float diff = i - m
float weight = math.exp(-(diff * diff) / s2)
sum += price * weight
weight_sum += weight
nz(sum / weight_sum, source)
// ---------- Main loop ----------
// Inputs
i_period = input.int(50, "Period", minval=1, tooltip="Number of bars used in the calculation")
i_offset = input.float(0.85, "Offset", minval=0.0, maxval=1.0, step=0.01)
i_sigma = input.float(6.0, "Sigma", minval=0.1, maxval=20.0, step=0.1)
i_source = input.source(close, "Source")
// Calculation
alma_value = alma(i_source, i_period, i_offset, i_sigma)
// Plot
plot(alma_value, "ALMA", color=color.yellow, linewidth=2)