Files
Miha Kralj 35a6702b06 fix(docs): correct .md documentation across errors, dynamics, filters, forecasts, momentum, numerics, oscillators, reversals, statistics, trends, volatility, volume
Deep review of all indicator categories verified .md headers against .cs WarmupPeriod, parameters, inputs, and outputs. Fixes include warmup corrections, parameter documentation, output type accuracy, and Pine Script alignment.
2026-03-10 18:38:23 -07:00

70 lines
3.2 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("NMA - Natural Moving Average", "NMA", overlay=true)
// ── Functions ──────────────────────────────────────────────────────────
// @function Calculates the Natural Moving Average (Jim Sloman, Ocean Theory pgs 63-70).
// Adaptive IIR filter where the smoothing ratio derives from volatility-weighted
// sqrt-kernel analysis of log-price movements over a lookback window.
// Step 1: ln = log(src) × 1000 (scaled natural log)
// Step 2: For i=0..period-1, accumulate:
// oi = |ln[i] - ln[i+1]| (bar-to-bar log-price volatility)
// num += oi × (√(i+1) - √i) (sqrt-differenced weight emphasizes recent bars)
// denom += oi (total volatility normalization)
// Step 3: ratio = num / denom (adaptive smoothing factor ∈ [0,1])
// Step 4: nma = nma[1] + ratio × (src - nma[1]) (IIR adaptive EMA step)
// When volatility concentrates in recent bars → ratio ≈ 1 → fast tracking.
// When volatility is spread uniformly → ratio ≈ 1/√period → heavy smoothing.
// @param source Series to smooth
// @param period Lookback window for volatility analysis (must be > 0)
// @returns NMA value
nma(series float source, simple int period) =>
float src = nz(source)
// Step 1: scaled natural log of price
// Use circular buffer to store log-scaled values for lookback
var array<float> lnBuf = array.new_float(period + 1, 0.0)
var int head = 0
float lnVal = src > 0 ? math.log(src) * 1000.0 : 0.0
array.set(lnBuf, head, lnVal)
// Step 2: compute volatility-weighted sqrt ratio over lookback
float num = 0.0
float denom = 0.0
int bars = math.min(bar_index + 1, period)
for i = 0 to bars - 1
int idx0 = (head - i + period + 1) % (period + 1)
int idx1 = (head - i - 1 + period + 1) % (period + 1)
float oi = math.abs(array.get(lnBuf, idx0) - array.get(lnBuf, idx1))
num += oi * (math.sqrt(i + 1) - math.sqrt(i))
denom += oi
// Advance head for next bar
head := (head + 1) % (period + 1)
// Step 3: adaptive ratio
float ratio = denom != 0.0 ? num / denom : 0.0
// Step 4: IIR adaptive EMA step
var float result = 0.0
if bar_index == 0
result := src
else
result := result + ratio * (src - result)
result
// ── Inputs ─────────────────────────────────────────────────────────────
int i_period = input.int(40, "Period", minval=1)
float i_source = input.source(close, "Source")
// ── Calculation ────────────────────────────────────────────────────────
float value = nma(i_source, i_period)
// ── Plot ───────────────────────────────────────────────────────────────
plot(value, "NMA", color.yellow, 2)