Files
QuanTAlib/lib/numerics/normdist/normdist.pine
T
Miha Kralj 90d5638008 Add new moving average implementations: LTMA, MCNMA, NLMA, NMA, NYQMA, RAIN, and TRAMA
- LTMA (Linear Trend Moving Average): Introduces a predictive moving average using dual cascaded EMAs for trend estimation.
- MCNMA (McNicholl EMA): Implements a zero-lag TEMA using a cascaded EMA structure for enhanced responsiveness.
- NLMA (Non-Lag Moving Average): Utilizes a damped cosine kernel to achieve reduced lag in moving averages.
- NMA (Natural Moving Average): Adapts smoothing based on volatility profiles using a square-root kernel.
- NYQMA (Nyquist Moving Average): Applies the Nyquist-Shannon theorem to prevent aliasing in cascaded moving averages.
- RAIN (Rainbow Moving Average): Combines multiple SMA layers with weighted averages for multi-scale smoothing.
- TRAMA (Trend Regularity Adaptive Moving Average): Adapts smoothing based on the frequency of new highs and lows in price data.
2026-02-20 21:40:32 -08:00

81 lines
3.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("Normal Distribution CDF (NORMDIST)", "NORMDIST", overlay=false, precision=6)
//@function Computes Normal Distribution CDF for a normalized price series
//@param source Series to transform
//@param period Lookback period for z-score normalization
//@param mu Mean parameter (0.0 for standard normal after z-score)
//@param sigma Standard deviation parameter (1.0 for standard normal after z-score)
//@returns CDF value in [0,1]: Φ(z) = 0.5 × (1 + erf(z / √2))
//@optimized O(period) per bar for mean/variance scan; CDF itself is O(1)
normdist(series float source, simple int period, simple float mu, simple float sigma) =>
if period <= 0
runtime.error("Period must be greater than 0")
if sigma <= 0.0
runtime.error("Sigma must be greater than 0")
// Compute rolling mean and standard deviation over lookback
float sum = 0.0
float sumSq = 0.0
int count = 0
for i = 0 to period - 1
float v = source[i]
if not na(v)
sum += v
sumSq += v * v
count += 1
float result = 0.5
if count >= 2
float mean = sum / count
float variance = (sumSq / count) - (mean * mean)
float stddev = variance > 0.0 ? math.sqrt(variance) : 0.0
// Z-score: normalize source relative to its own rolling distribution
float z = stddev > 0.0 ? (source - mean) / stddev : 0.0
// Apply user-specified mu/sigma shift: z_final = (z - mu) / sigma
float z_final = (z - mu) / sigma
// Approximate erf via Abramowitz & Stegun (max error < 1.5e-7)
// erf(x) = 1 - (a1*t + a2*t^2 + a3*t^3) * exp(-x^2)
// where t = 1 / (1 + 0.47047 * |x|)
float x = z_final / math.sqrt(2.0)
float ax = math.abs(x)
float t = 1.0 / (1.0 + 0.47047 * ax)
float t2 = t * t
float t3 = t2 * t
float a1 = 0.3480242
float a2 = -0.0958798
float a3 = 0.7478556
float erfApprox = 1.0 - (a1 * t + a2 * t2 + a3 * t3) * math.exp(-(ax * ax))
float erf = x >= 0.0 ? erfApprox : -erfApprox
// CDF: Φ(z) = 0.5 * (1 + erf(z / sqrt(2)))
result := 0.5 * (1.0 + erf)
result
// ---------- Main loop ----------
// Inputs
i_source = input.source(close, "Source")
i_period = input.int(50, "Lookback Period", minval=2, maxval=5000, tooltip="Rolling window for z-score normalization")
i_mu = input.float(0.0, "Mu (μ)", step=0.1, tooltip="Mean shift parameter (0 = standard normal)")
i_sigma = input.float(1.0, "Sigma (σ)", minval=0.01, step=0.1, tooltip="Scale parameter (1 = standard normal)")
// Calculation
float result = normdist(i_source, i_period, i_mu, i_sigma)
// Plot
plot(result, "NORMDIST", color=color.yellow, linewidth=2)
hline(0.5, "Midline", color=color.gray, linestyle=hline.style_dotted)
hline(0.975, "Upper 2σ", color=color.red, linestyle=hline.style_dashed)
hline(0.025, "Lower 2σ", color=color.green, linestyle=hline.style_dashed)
hline(0.841, "Upper 1σ", color=color.orange, linestyle=hline.style_dashed)
hline(0.159, "Lower 1σ", color=color.teal, linestyle=hline.style_dashed)