mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-21 12:08:05 +00:00
77 lines
3.6 KiB
Plaintext
77 lines
3.6 KiB
Plaintext
// 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)
|