// Licensed under the Apache License, Version 2.0 // © mihakralj //@version=6 indicator("Bessel-Weighted Moving Average (BWMA)", "BWMA", overlay=true) //@function Calculates BWMA using Bessel window weighting //@param source Series to calculate BWMA from //@param period Lookback period - FIR window size //@param order Bessel function order (default: 0) //@returns BWMA value, calculates from first bar using available data //@optimized Uses Bessel window coefficients with O(n) complexity per bar due to lookback loop bwma(series float source, simple int period, simple int order=0) => if period <= 0 runtime.error("Period must be greater than 0") if order < 0 runtime.error("Bessel order must be non-negative") int p = math.min(bar_index + 1, period) var array weights = array.new_float(1, 1.0) var int last_p = 1 var int last_order = order if last_p != p or last_order != order weights := array.new_float(p, 0.0) float total_weight = 0.0 float scale = 2.0 / (p - 1) float power = order / 2.0 + 0.5 for i = 0 to p - 1 float x = i * scale - 1.0 float arg = 1.0 - x * x float w = 0.0 if arg > 0.0 if order == 0 w := arg else if order == 1 w := arg * math.sqrt(arg) else w := math.pow(arg, power) array.set(weights, i, w) total_weight += w if total_weight > 0.0 float inv_total = 1.0 / total_weight for i = 0 to p - 1 array.set(weights, i, array.get(weights, i) * inv_total) last_p := p last_order := order float sum = 0.0 float weight_sum = 0.0 for i = 0 to p - 1 float price = source[i] if not na(price) float w = array.get(weights, i) sum += price * w weight_sum += w nz(sum / weight_sum, source) // ---------- Main loop ---------- // Inputs i_period = input.int(10, "Period", minval=1) i_order = input.int(0, "Bessel Order", minval=0, maxval=3, tooltip="Order of the Bessel function (0-3)") i_source = input.source(close, "Source") // Calculation bwma_value = bwma(i_source, i_period, i_order) // Plot plot(bwma_value, "BWMA", color=color.yellow, linewidth=2)