// 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)