Files
QuanTAlib/lib/trends_FIR/nlma/nlma.pine
T
Miha Kralj 7253f61299 Add TRAMA implementation and comprehensive tests
- Implemented the TRAMA (Trend Regularity Adaptive Moving Average) class with adaptive EMA logic.
- Added unit tests for TRAMA functionality, including constructor validation, basic calculations, state management, and robustness checks.
- Created validation tests to ensure consistency across different modes of operation (streaming, batch, and static calculations).
- Enhanced documentation for TRAMA, including performance profiles and quality metrics.
- Updated workspace configuration by removing unnecessary folder references.
2026-02-21 20:45:38 -08:00

54 lines
2.1 KiB
Plaintext
Raw 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.
// The MIT License (MIT)
// © mihakralj
//@version=6
indicator("Non-Lag Moving Average (NLMA)", "NLMA", overlay=true)
//@function Calculates NLMA using damped cosine (fading sinusoid) FIR kernel
//@param source Series to calculate NLMA from
//@param period Lookback period - defines the cycle length of the cosine kernel
//@returns NLMA value, calculates from first bar using available data
//@description NonLagMA by Igorad (TrendLaboratory). Uses a damped cosine kernel
// (fading sinusoid) derived from FATL/SATL digital filter coefficient analysis.
// The cosine creates negative weights that subtract lagged price components,
// reducing lag while maintaining smoothness. Weight formula:
// w[i] = cos(2*PI*i / cycle) * (1 - i/cycle)
// where cycle = period, creating one full cosine oscillation with linear decay.
// Negative weights in the mid-section cancel lag (analogous to DEMA's 2*EMA-EMA2).
// Normalization by signed weight sum preserves DC gain = 1.
nlma(series float source, simple int period) =>
if period <= 0
runtime.error("Period must be greater than 0")
int p = math.min(bar_index + 1, period)
var int prev_p = 0
var array<float> cos_weights = array.new_float(period, 0.0)
// Recompute weights when effective period changes (warmup)
if p != prev_p
cos_weights := array.new_float(p, 0.0)
for j = 0 to p - 1
// Damped cosine: cosine oscillation × linear decay envelope
float angle = 2.0 * math.pi * j / p
float decay = 1.0 - float(j) / float(p)
array.set(cos_weights, j, math.cos(angle) * decay)
prev_p := p
float sum_wv = 0.0
float sum_w = 0.0
for i = 0 to p - 1
float price = source[i]
if not na(price)
float w = array.get(cos_weights, i)
sum_wv += price * w
sum_w += w
nz(sum_wv / sum_w, source)
// ---------- Main loop ----------
// Inputs
i_period = input.int(14, "Period", minval=1)
i_source = input.source(close, "Source")
// Calculation
nlma_value = nlma(i_source, i_period)
// Plot
plot(nlma_value, "NLMA", color=color.yellow, linewidth=2)