feat: add new indicators (Decay, Edecay, MinusDi, MinusDm, PlusDi, PlusDm, Maxindex, Minindex, Sarext) and update pine scripts, core libs, validation tests, and python bindings
2026-03-09 13:45:46 -07:00
|
|
|
|
// Licensed under the Apache License, Version 2.0
|
2026-02-20 18:44:56 -08:00
|
|
|
|
// © mihakralj
|
|
|
|
|
|
|
|
|
|
|
|
//@version=6
|
|
|
|
|
|
indicator("MCNMA - McNicholl EMA", "MCNMA", overlay=true)
|
|
|
|
|
|
|
2026-02-23 17:27:35 -08:00
|
|
|
|
//@function Calculates the McNicholl EMA (Zero-Lag TEMA).
|
|
|
|
|
|
// Dennis McNicholl, "Better Bollinger Bands," Futures Magazine, October 1998.
|
|
|
|
|
|
// MCNMA = 2·TEMA(src,N) − TEMA(TEMA(src,N),N)
|
|
|
|
|
|
// TEMA(x,N) = 3·EMA1 − 3·EMA2 + EMA3
|
|
|
|
|
|
// Six cascaded EMA stages; na-guard init to source eliminates warmup bias.
|
|
|
|
|
|
//@param source Series to smooth
|
|
|
|
|
|
//@param period Lookback period (must be > 0)
|
|
|
|
|
|
//@returns McNicholl EMA value from bar 1
|
|
|
|
|
|
mcnma(series float source, simple int period) =>
|
|
|
|
|
|
float src = nz(source)
|
2026-02-20 18:44:56 -08:00
|
|
|
|
float alpha = 2.0 / (period + 1)
|
2026-02-23 17:27:35 -08:00
|
|
|
|
float beta = 1.0 - alpha
|
2026-02-20 18:44:56 -08:00
|
|
|
|
|
2026-02-23 17:27:35 -08:00
|
|
|
|
var float e1 = na
|
|
|
|
|
|
var float e2 = na
|
|
|
|
|
|
var float e3 = na
|
|
|
|
|
|
var float e4 = na
|
|
|
|
|
|
var float e5 = na
|
|
|
|
|
|
var float e6 = na
|
2026-02-20 18:44:56 -08:00
|
|
|
|
|
2026-02-23 17:27:35 -08:00
|
|
|
|
if na(e1)
|
|
|
|
|
|
e1 := src
|
|
|
|
|
|
e2 := src
|
|
|
|
|
|
e3 := src
|
|
|
|
|
|
e4 := src
|
|
|
|
|
|
e5 := src
|
|
|
|
|
|
e6 := src
|
|
|
|
|
|
else
|
|
|
|
|
|
e1 := alpha * src + beta * e1
|
|
|
|
|
|
e2 := alpha * e1 + beta * e2
|
|
|
|
|
|
e3 := alpha * e2 + beta * e3
|
2026-02-20 18:44:56 -08:00
|
|
|
|
|
2026-02-23 17:27:35 -08:00
|
|
|
|
float tema1 = 3.0 * e1 - 3.0 * e2 + e3
|
2026-02-20 18:44:56 -08:00
|
|
|
|
|
2026-02-23 17:27:35 -08:00
|
|
|
|
e4 := alpha * tema1 + beta * e4
|
|
|
|
|
|
e5 := alpha * e4 + beta * e5
|
|
|
|
|
|
e6 := alpha * e5 + beta * e6
|
2026-02-20 18:44:56 -08:00
|
|
|
|
|
2026-02-23 17:27:35 -08:00
|
|
|
|
float tema2 = 3.0 * e4 - 3.0 * e5 + e6
|
2026-02-20 18:44:56 -08:00
|
|
|
|
|
2026-02-23 17:27:35 -08:00
|
|
|
|
// result is assigned below — but we need it outside the else
|
|
|
|
|
|
// Pine requires expression, so use a different structure:
|
2026-02-20 18:44:56 -08:00
|
|
|
|
|
2026-02-23 17:27:35 -08:00
|
|
|
|
// recompute after update (valid on all bars after init)
|
|
|
|
|
|
float tema1 = 3.0 * e1 - 3.0 * e2 + e3
|
|
|
|
|
|
float tema2 = 3.0 * e4 - 3.0 * e5 + e6
|
|
|
|
|
|
2.0 * tema1 - tema2
|
2026-02-20 18:44:56 -08:00
|
|
|
|
|
|
|
|
|
|
// ── Inputs ─────────────────────────────────────────────────────────────
|
2026-02-23 17:27:35 -08:00
|
|
|
|
int i_period = input.int(14, "Period", minval=1)
|
|
|
|
|
|
float i_source = input.source(close, "Source")
|
2026-02-20 18:44:56 -08:00
|
|
|
|
|
|
|
|
|
|
// ── Calculation ────────────────────────────────────────────────────────
|
|
|
|
|
|
float value = mcnma(i_source, i_period)
|
|
|
|
|
|
|
|
|
|
|
|
// ── Plot ───────────────────────────────────────────────────────────────
|
2026-02-23 17:27:35 -08:00
|
|
|
|
plot(value, "MCNMA", color.yellow, 2)
|