Files
QuanTAlib/lib/trends_IIR/hw/hw.pine
T

77 lines
3.6 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("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)