// The MIT License (MIT) // © mihakralj //@version=6 indicator("Holt-Winters Triple Smoothing (HW)", "HW", overlay=true) //@function Computes Holt-Winters Triple Exponential Smoothing with level (F), // velocity (V), and acceleration (A) components. Extends Holt's double // smoothing by adding a second-order derivative tracker for curvature. // Output forecasts using F + V + 0.5×A (Taylor expansion to second order). //@param source Series to smooth //@param period Lookback period (determines alpha=2/(period+1), beta=gamma=1/period) //@param na Alpha override: level smoothing (0..1, 0=auto) //@param nb Beta override: velocity smoothing (0..1, 0=auto) //@param ng Gamma override: acceleration smoothing (0..1, 0=auto) //@returns Holt-Winters smoothed value from first bar //@reference Winters, P.R. (1960). "Forecasting Sales by Exponentially Weighted // Moving Averages." Management Science, 6(3), 324-342. //@reference Holt, C.C. (1957/2004). "Forecasting Seasonals and Trends by // Exponentially Weighted Moving Averages." International Journal of // Forecasting, 20(1), 5-10. //@optimized O(1) per bar — three IIR state variables with FMA-equivalent updates hw(series float source, simple int period, simple float na=0, simple float nb=0, simple float ng=0) => if period <= 0 runtime.error("Period must be greater than 0") float price = nz(source) // Smoothing factors: auto-derive from period if overrides are 0 float alpha = na > 0 and na <= 1 ? na : 2.0 / (period + 1) float beta = nb > 0 and nb <= 1 ? nb : 1.0 / period float gamma = ng > 0 and ng <= 1 ? ng : 1.0 / period float decayA = 1.0 - alpha float decayB = 1.0 - beta float decayG = 1.0 - gamma // State: level (F), velocity (V), acceleration (A) var float F = na var float V = 0.0 var float A = 0.0 if na(F) // First bar: initialize level to source, velocity and acceleration to 0 F := price V := 0.0 A := 0.0 price else float prevF = F float prevV = V float prevA = A // Level: F = alpha * source + (1 - alpha) * (prevF + prevV + 0.5 * prevA) float forecast = prevF + prevV + 0.5 * prevA F := alpha * price + decayA * forecast // Velocity: V = beta * (F - prevF) + (1 - beta) * (prevV + prevA) V := beta * (F - prevF) + decayB * (prevV + prevA) // Acceleration: A = gamma * (V - prevV) + (1 - gamma) * prevA A := gamma * (V - prevV) + decayG * prevA // Output: F + V + 0.5 * A (second-order Taylor forecast) F + V + 0.5 * A // ── Inputs ────────────────────────────────────────────────────────────── src = input.source(close, "Source") per = input.int(10, "Period", minval=1) i_alpha = input.float(0, "Alpha (0=auto)", minval=0, maxval=1, step=0.01, tooltip="Level smoothing. 0 = 2/(period+1)") i_beta = input.float(0, "Beta (0=auto)", minval=0, maxval=1, step=0.01, tooltip="Velocity smoothing. 0 = 1/period") i_gamma = input.float(0, "Gamma (0=auto)", minval=0, maxval=1, step=0.01, tooltip="Acceleration smoothing. 0 = 1/period") // ── Plot ──────────────────────────────────────────────────────────────── plot(hw(src, per, i_alpha, i_beta, i_gamma), "HW", color.new(color.yellow, 0), 2)