# SP15: Spencer 15-Point Moving Average | Property | Value | | ---------------- | -------------------------------- | | **Category** | Trend (FIR MA) | | **Inputs** | Source (close) | | **Parameters** | None | | **Outputs** | Single series (SP15) | | **Output range** | Tracks input | | **Warmup** | `Period` bars | ### TL;DR - SP15 is a fixed-coefficient symmetric FIR filter with 15 weights: $[-3, -6, -5, 3, 21, 46, 67, 74, 67, 46, 21, 3, -5, -6, -3]$ divided by 320. - No configurable parameters; computation is stateless per bar. - Output range: Tracks input. - Requires `Period` bars of warmup before first valid output (IsHot = true). - Validated against TA-Lib, Skender, and Tulip reference implementations where available. > "John Spencer designed 15 weights that zero out quarterly and quintile seasonality from economic data. Eighty years later, statisticians still reach for them when they need a quick seasonal adjustment that does not require the German engineering of X-13ARIMA." SP15 is a fixed-coefficient symmetric FIR filter with 15 weights: $[-3, -6, -5, 3, 21, 46, 67, 74, 67, 46, 21, 3, -5, -6, -3]$ divided by 320. The weights were designed by John Spencer to have zero frequency response at periods 4 and 5 (frequencies $2\pi/4$ and $2\pi/5$), making the filter effective at removing quarterly and quintile seasonal components from economic time series. The negative edge weights provide bandpass-like characteristics, and the fixed design requires no parameters beyond the source series. ## Historical Context John Spencer published the 15-point and 21-point weighted moving averages in 1904 for use in actuarial graduation (smoothing mortality tables). The weights were constructed to satisfy two constraints simultaneously: (1) preserve polynomial trends up to degree 3 (cubic), and (2) have zero response at specific seasonal frequencies. The 15-point variant zeros out periods 4 and 5; the 21-point variant zeros out periods 4, 5, and 7. Spencer's filters predated Henderson's (1916) by twelve years and were widely used in actuarial science and economic statistics before the X-11 method standardized on Henderson filters. The Spencer 15-point filter was the default seasonal adjustment tool at the U.K. Office for National Statistics until the adoption of X-11 in the 1960s. In modern practice, it remains useful as a quick-and-dirty seasonal smoother when full X-13ARIMA decomposition is overkill. The fixed 15-bar length creates a natural centered lag of 7 bars, which is appropriate for quarterly data (4 observations per year, so a 15-point filter spans nearly 4 quarters). For financial time series, the filter useful for removing intra-week (5-bar) and intra-month patterns from daily data. ## Architecture & Physics ### 1. Fixed Weight Vector The 15 weights are hardcoded constants, symmetric around the center: $$ \mathbf{w} = \frac{1}{320}[-3, -6, -5, 3, 21, 46, 67, 74, 67, 46, 21, 3, -5, -6, -3] $$ No weight computation is needed; the coefficients are compile-time constants. ### 2. Symmetric Convolution The symmetric structure allows folded computation: pair the $i$-th and $(14-i)$-th bars (which share the same weight), sum them, then multiply by the weight once. This halves the multiplication count from 15 to 8. ### 3. Negative Edge Weights Three weights at each edge are negative ($-3, -6, -5$), giving the filter its seasonal-nulling property. The output can exceed the input range when edge bars have extreme values relative to the center. ### 4. Zero-Parameter Design SP15 takes no period parameter. The filter length is always 15, and the weights are always Spencer's original values. This is both a strength (no tuning required) and a limitation (no adaptation to different data characteristics). ## Mathematical Foundation The Spencer 15-point filter output: $$ \text{SP15}_t = \frac{1}{320}\sum_{j=0}^{14} w_j \cdot x_{t-j} $$ Exploiting symmetry ($w_j = w_{14-j}$): $$ \text{SP15}_t = \frac{1}{320}\left[w_7 \cdot x_{t-7} + \sum_{j=0}^{6} w_j \left(x_{t-j} + x_{t-14+j}\right)\right] $$ **Frequency response zeros:** $$ H\left(e^{j2\pi/4}\right) = 0, \quad H\left(e^{j2\pi/5}\right) = 0 $$ These zeros ensure complete suppression of periodicities at 4 and 5 bars. **Weight sum:** $-3-6-5+3+21+46+67+74+67+46+21+3-5-6-3 = 320$ **Polynomial preservation:** The filter preserves polynomials up to degree 3: $$ \sum_{j=0}^{14} w_j \cdot (j-7)^k = 320 \cdot \delta_{k0}, \quad k = 0, 1, 2, 3 $$ **Default parameters:** None (fixed 15-point filter). **Pseudo-code (streaming):** ``` // Fixed symmetric weights (compile-time constants) w = [-3, -6, -5, 3, 21, 46, 67, 74, 67, 46, 21, 3, -5, -6, -3] // Symmetric folded computation total = w[7] * src[7] for j = 0 to 6: total += w[j] * (src[j] + src[14-j]) return total / 320 ``` ## Resources - Spencer, J. (1904). "On the Graduation of the Rates of Sickness and Mortality." *Journal of the Institute of Actuaries*, 38, 334-343. - Macaulay, F.R. (1931). *The Smoothing of Time Series.* NBER. Chapter 4: Spencer-Type Formulas. - Kendall, M.G. & Stuart, A. (1976). *The Advanced Theory of Statistics*, Vol. 3, 3rd ed. Griffin. Section 46.13: Spencer's Formulae. - Kenny, P.B. & Durbin, J. (1982). "Local Trend Estimation and Seasonal Adjustment of Economic and Social Time Series." *JRSS Series A*, 145(1). ## Performance Profile ### Operation Count (Streaming Mode) SP15 is a fixed 15-tap FIR filter with hard-coded Spencer weights (sum = 320). At construction, 15 normalized doubles are computed once. Each `Update()` is a pure 15-element dot product. | Operation | Count | Cost (cycles) | Subtotal | | :--- | :---: | :---: | :---: | | Ring buffer push | 1 | 3 | ~3 | | FIR dot product: 15 FMA | 15 | 4 | ~60 | | **Total** | **16** | — | **~63 cycles** | O(1) per bar (N is fixed at 15). The dot product takes ~60 cycles on modern x86. WarmupPeriod = 15. No parameters to validate. ### Batch Mode (SIMD Analysis) | Operation | Vectorizable? | Notes | | :--- | :---: | :--- | | 15-tap FIR convolution | Yes | AVX2: 4 `VFMADD231PD` passes cover 16 taps (1 unused) | | Symmetric weights [−3,−6,−5,3,21,46,67,74,…] | Yes | Symmetric: fold to 8 unique weights; 8 FMADs per bar | | Negative edge weights | Yes | Signed FMA; no special masking | | Fixed-N: 15 taps | Yes | Compiler can fully unroll the 15-FMA loop at O3 | With symmetric folding (8 unique weight pairs), the 15-tap dot product reduces to ~8 FMAs. AVX2 processes 4 output bars per outer iteration. Batch throughput: ~2 cycles per output bar at peak. Unrolled codegen fits entirely in instruction cache.